Merge pull request #337 feature-NEWDWH2021-1408 into develop-fix-webapp-vulnerability
This commit is contained in:
commit
1738b406ba
@ -84,6 +84,8 @@
|
||||
│ ├── exception_handler.py -- FastAPI内部でエラー発生時のハンドリング
|
||||
│ └── exceptions.py -- カスタム例外クラス
|
||||
├── main.py -- APサーバーのエントリーポイント。ここでルーターやハンドラーの登録を行う
|
||||
├── middleware -- ミドルウェアの設定
|
||||
│ └── middleware.py
|
||||
├── model -- モデル層(MVCのM)
|
||||
│ ├── db -- リポジトリから返されるDBレコードのモデル
|
||||
│ │ ├── base_db_model.py
|
||||
@ -195,3 +197,39 @@
|
||||
- コントローラーのrouter変数が、`router.route_class = Authenticate`となっている場合、以下の動きをする
|
||||
- リクエスト到達時にセッションの有無をチェックする
|
||||
- レスポンス時、クッキーにセッションキーを登録する
|
||||
|
||||
## HTMLで読み込んでいるスクリプトのSRIハッシュ値を生成・設定する方法
|
||||
|
||||
### サブリソース完全性 (Subresource Integrity, SRI) とは
|
||||
|
||||
CDN などから取得したリソースが意図せず改ざんされていないかをブラウザーが検証するセキュリティ機能です。 SRI を利用する際には、取得したリソースのハッシュ値と一致すべきハッシュ値を指定します。
|
||||
|
||||
詳細:<https://developer.mozilla.org/ja/docs/Web/Security/Subresource_Integrity>
|
||||
|
||||
実消化&アルトマークのWebアプリケーションでは、複数の外部スクリプトを読み込んで動作しているため、読み込むスクリプトを変更した場合は、
|
||||
タグの属性値`integrity`に設定されているスクリプトのハッシュ値を更新する必要がある。
|
||||
|
||||
### SRI ハッシュ値の生成方法(サーバー内のスクリプトについて)
|
||||
|
||||
- サーバー内に保管されているスクリプトを更新した場合、Linux環境(WSL2でも可)で、以下のコマンドを実行し、ハッシュ値を生成する
|
||||
|
||||
```bash
|
||||
cat <更新したスクリプトファイル名> | openssl dgst -sha384 -binary | openssl base64 -A
|
||||
```
|
||||
|
||||
参考:<https://developer.mozilla.org/ja/docs/Web/Security/Subresource_Integrity#sri_%E3%83%8F%E3%83%83%E3%82%B7%E3%83%A5%E3%82%92%E7%94%9F%E6%88%90%E3%81%99%E3%82%8B%E3%83%84%E3%83%BC%E3%83%AB>
|
||||
|
||||
|
||||
### SRI ハッシュ値の生成方法(外部サイトから読み込んでいるスクリプトについて)
|
||||
|
||||
- 外部サイトから読み込んでいるスクリプトを更新した場合、下記のMDNオンラインツールでハッシュ値を生成する
|
||||
- [SRI Hash Generator](https://www.srihash.org/)
|
||||
|
||||
### SRI ハッシュ値の設定方法
|
||||
|
||||
- 更新したスクリプトを読み込んでいる箇所の`integrity`属性値を、生成したハッシュ値に置き換える
|
||||
- 以下は設定のサンプル
|
||||
|
||||
```bash
|
||||
<script src="https://リンク/スクリプト.js" integrity="sha384-生成したハッシュ" crossorigin="anonymous"></script>
|
||||
```
|
||||
|
||||
@ -79,6 +79,7 @@ def search_bio_data(
|
||||
'data': data,
|
||||
'count': bio_sales_lot_count
|
||||
})
|
||||
|
||||
# クッキーも書き換え
|
||||
json_response.set_cookie(
|
||||
key='session',
|
||||
@ -152,6 +153,7 @@ async def download_bio_data(
|
||||
'status': 'ok',
|
||||
'download_url': download_file_url
|
||||
})
|
||||
|
||||
json_response.set_cookie(
|
||||
key='session',
|
||||
value=session.session_key,
|
||||
|
||||
@ -113,6 +113,7 @@ def login(
|
||||
status_code=status.HTTP_303_SEE_OTHER,
|
||||
headers={'session_key': session_key}
|
||||
)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@ -170,4 +171,5 @@ def sso_authorize(
|
||||
status_code=status.HTTP_303_SEE_OTHER,
|
||||
headers={'session_key': session_key}
|
||||
)
|
||||
|
||||
return response
|
||||
|
||||
@ -10,6 +10,7 @@ from src.controller import (bio, bio_api, healthcheck, login, logout,
|
||||
from src.core import task
|
||||
from src.error.exception_handler import http_exception_handler
|
||||
from src.error.exceptions import UnexpectedException
|
||||
from src.middleware.middleware import SecurityHeadersMiddleware
|
||||
|
||||
app = FastAPI(openapi_url=None)
|
||||
|
||||
@ -42,5 +43,8 @@ app.add_exception_handler(status.HTTP_403_FORBIDDEN, http_exception_handler)
|
||||
# サーバーエラーが発生した場合のハンドラー。HTTPExceptionではハンドリングできないため、個別に設定
|
||||
app.add_exception_handler(UnexpectedException, http_exception_handler)
|
||||
|
||||
# セキュリティヘッダー設定はミドルウェアで処理する
|
||||
app.add_middleware(SecurityHeadersMiddleware)
|
||||
|
||||
# サーバー起動時のイベント
|
||||
app.add_event_handler('startup', task.create_start_app_handler())
|
||||
|
||||
0
ecs/jskult-webapp/src/middleware/__init__.py
Normal file
0
ecs/jskult-webapp/src/middleware/__init__.py
Normal file
16
ecs/jskult-webapp/src/middleware/middleware.py
Normal file
16
ecs/jskult-webapp/src/middleware/middleware.py
Normal file
@ -0,0 +1,16 @@
|
||||
from fastapi import Request, Response, status
|
||||
from fastapi.responses import JSONResponse
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
|
||||
class SecurityHeadersMiddleware(BaseHTTPMiddleware):
|
||||
async def dispatch(self, request, call_next):
|
||||
response = await call_next(request)
|
||||
# X-Frame-Optionsヘッダー追加
|
||||
response.headers['X-Frame-Options'] = 'DENY'
|
||||
# X-Content-Type-Optionsヘッダー追加
|
||||
response.headers['X-Content-Type-Options'] = 'nosniff'
|
||||
# Strict-Transport-Securityヘッダー追加
|
||||
response.headers['Strict-Transport-Security'] = 'max-age=31536000 includeSubDomains'
|
||||
# Cache-Controlヘッダー追加
|
||||
response.headers['Cache-Control'] = 'private'
|
||||
return response
|
||||
@ -103,6 +103,7 @@ class AfterSetCookieSessionRoute(MeDaCaRoute):
|
||||
"""事後処理として、セッションキーをcookieに設定するカスタムルートハンドラー"""
|
||||
async def post_process_route(self, request: Request, response: Response):
|
||||
response = await super().post_process_route(request, response)
|
||||
|
||||
session_key = response.headers.get('session_key', None)
|
||||
# セッションキーがない場合はセットせずに返す
|
||||
if session_key is None:
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="format-detection" content="telephone=no, address=no" http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<meta name="format-detection" content="telephone=no, address=no" />
|
||||
<title>{{subtitle}}</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.2/font/bootstrap-icons.css" integrity="sha384-b6lVK+yci+bfDmaY1u0zE8YYJt0TZxLEAFyYSLHId4xoVvsrQu3INevFKo+Xir8e" crossorigin="anonymous">
|
||||
@ -12,8 +13,8 @@
|
||||
<link rel="stylesheet" href="/static/css/loading.css">
|
||||
<script src="https://code.jquery.com/jquery-3.6.3.min.js" integrity="sha256-pvPw+upLPUjgMXY0G+8O0xUf+/Im1MZjXxxgOcBQBXU=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN" crossorigin="anonymous"></script>
|
||||
<script src="https://pagination.js.org/dist/2.5.0/pagination.min.js" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/flatpickr/dist/l10n/ja.min.js"></script>
|
||||
<script src="/static/function/businessLogicScript.js"></script>
|
||||
<script src="/static/lib/fixed_midashi.js"></script>
|
||||
<script src="https://pagination.js.org/dist/2.5.0/pagination.min.js" integrity="sha384-hAMOwOF47ClZBKl6rGGjHx8uo+4cTDSUT97JzDVolMwHHNG+2CkQH3yAv8Js08o0" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.js" integrity="sha384-5JqMv4L/Xa0hfvtF06qboNdhvuYXUku9ZrhZh3bSk8VXF0A/RuSLHpLsSV9Zqhl6" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/flatpickr/dist/l10n/ja.min.js" integrity="sha384-camx8EVof9bxBiCoveSA0vaNCuM6kzDC/01eqIoob2qT6wjBN7UP+qQmYOS3mN1x" crossorigin="anonymous"></script>
|
||||
<script src="/static/function/businessLogicScript.js" integrity="sha384-ytd1o7Rx4BPzjO3RpzR9fW/Z4avGzS7+BRPZVUsQp5X4zXB6xdZpR47/En1mNl7s" crossorigin="anonymous"></script>
|
||||
<script src="/static/lib/fixed_midashi.js" integrity="sha384-mCd6L3DNaLgUWyH051BywJfzlVavCkK6F0wbMqG+j7jAq174Uf7HJdq3H4wxCJKs" crossorigin="anonymous"></script>
|
||||
Loading…
x
Reference in New Issue
Block a user