diff --git a/ecs/jskult-webapp/README.md b/ecs/jskult-webapp/README.md
index 97632b8f..c65cbf83 100644
--- a/ecs/jskult-webapp/README.md
+++ b/ecs/jskult-webapp/README.md
@@ -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 を利用する際には、取得したリソースのハッシュ値と一致すべきハッシュ値を指定します。
+
+詳細:
+
+実消化&アルトマークのWebアプリケーションでは、複数の外部スクリプトを読み込んで動作しているため、読み込むスクリプトを変更した場合は、
+タグの属性値`integrity`に設定されているスクリプトのハッシュ値を更新する必要がある。
+
+### SRI ハッシュ値の生成方法(サーバー内のスクリプトについて)
+
+- サーバー内に保管されているスクリプトを更新した場合、Linux環境(WSL2でも可)で、以下のコマンドを実行し、ハッシュ値を生成する
+
+```bash
+cat <更新したスクリプトファイル名> | openssl dgst -sha384 -binary | openssl base64 -A
+```
+
+参考:
+
+
+### SRI ハッシュ値の生成方法(外部サイトから読み込んでいるスクリプトについて)
+
+- 外部サイトから読み込んでいるスクリプトを更新した場合、下記のMDNオンラインツールでハッシュ値を生成する
+ - [SRI Hash Generator](https://www.srihash.org/)
+
+### SRI ハッシュ値の設定方法
+
+- 更新したスクリプトを読み込んでいる箇所の`integrity`属性値を、生成したハッシュ値に置き換える
+- 以下は設定のサンプル
+
+```bash
+
+```
diff --git a/ecs/jskult-webapp/src/controller/bio_api.py b/ecs/jskult-webapp/src/controller/bio_api.py
index 982ef8d2..4989a50d 100644
--- a/ecs/jskult-webapp/src/controller/bio_api.py
+++ b/ecs/jskult-webapp/src/controller/bio_api.py
@@ -79,11 +79,11 @@ def search_bio_data(
'data': data,
'count': bio_sales_lot_count
})
+
# クッキーも書き換え
json_response.set_cookie(
key='session',
value=session.session_key,
- max_age=environment.SESSION_EXPIRE_MINUTE * 60, # cookieの有効期限は秒数指定なので、60秒をかける
secure=True,
httponly=True
)
@@ -153,10 +153,10 @@ async def download_bio_data(
'status': 'ok',
'download_url': download_file_url
})
+
json_response.set_cookie(
key='session',
value=session.session_key,
- max_age=environment.SESSION_EXPIRE_MINUTE * 60, # cookieの有効期限は秒数指定なので、60秒をかける
secure=True,
httponly=True
)
diff --git a/ecs/jskult-webapp/src/controller/login.py b/ecs/jskult-webapp/src/controller/login.py
index c8a5663c..5c8d904e 100644
--- a/ecs/jskult-webapp/src/controller/login.py
+++ b/ecs/jskult-webapp/src/controller/login.py
@@ -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
diff --git a/ecs/jskult-webapp/src/controller/logout.py b/ecs/jskult-webapp/src/controller/logout.py
index 8a633f6c..76d9ef60 100644
--- a/ecs/jskult-webapp/src/controller/logout.py
+++ b/ecs/jskult-webapp/src/controller/logout.py
@@ -1,50 +1,57 @@
-from typing import Optional, Union
-
-from fastapi import APIRouter, Depends, Request
-from fastapi.responses import HTMLResponse
-
-from src.depends.auth import get_current_session
-from src.model.internal.session import UserSession
-from src.model.view.logout_view_model import LogoutViewModel
-from src.system_var import constants
-from src.templates import templates
-
-router = APIRouter()
-
-#########################
-# Views #
-#########################
-
-
-@router.get('/', response_class=HTMLResponse)
-def logout_view(
- request: Request,
- reason: Optional[str] = None,
- session: Union[UserSession, None] = Depends(get_current_session)
-):
- # どういうルートでログインしたかを判断するため、refererを取得
- referer = request.headers.get('referer', '')
-
- redirect_to = '/login/userlogin'
- link_text = 'MeDaCA機能メニューへ'
- # セッションが切れておらず、メンテユーザである、またはメンテログイン画面から遷移した場合、メンテログイン画面に戻す
- if (session is not None and session.user_flg == str(constants.PERMISSION_ENABLED)) \
- or referer.endswith('maintlogin'):
- redirect_to = '/login/maintlogin'
- link_text = 'Login画面に戻る'
-
- logout = LogoutViewModel(
- redirect_to=redirect_to,
- reason=constants.LOGOUT_REASON_MESSAGE_MAP.get(reason, ''),
- link_text=link_text
- )
- template_response = templates.TemplateResponse(
- 'logout.html',
- {
- 'request': request,
- 'logout': logout,
- }
- )
- # クッキーを削除
- template_response.delete_cookie('session')
- return template_response
+from typing import Optional, Union
+
+from fastapi import APIRouter, Depends, Request
+from fastapi.responses import HTMLResponse
+
+from src.depends.auth import get_current_session
+from src.model.internal.session import UserSession
+from src.model.view.logout_view_model import LogoutViewModel
+from src.system_var import constants
+from src.templates import templates
+from src.services import session_service
+
+router = APIRouter()
+
+#########################
+# Views #
+#########################
+
+
+
+@router.get('/', response_class=HTMLResponse)
+def logout_view(
+ request: Request,
+ reason: Optional[str] = None,
+ session: Union[UserSession, None] = Depends(get_current_session)
+):
+ # どういうルートでログインしたかを判断するため、refererを取得
+ referer = request.headers.get('referer', '')
+
+ redirect_to = '/login/userlogin'
+ link_text = 'MeDaCA機能メニューへ'
+ # セッションが切れておらず、メンテユーザである、またはメンテログイン画面から遷移した場合、メンテログイン画面に戻す
+ if (session is not None and session.user_flg == str(constants.PERMISSION_ENABLED)) \
+ or referer.endswith('maintlogin'):
+ redirect_to = '/login/maintlogin'
+ link_text = 'Login画面に戻る'
+
+ logout = LogoutViewModel(
+ redirect_to=redirect_to,
+ reason=constants.LOGOUT_REASON_MESSAGE_MAP.get(reason, ''),
+ link_text=link_text
+ )
+ template_response = templates.TemplateResponse(
+ 'logout.html',
+ {
+ 'request': request,
+ 'logout': logout,
+ }
+ )
+ # クッキーを削除
+ template_response.delete_cookie('session')
+
+ # セッション削除
+ if session:
+ session_service.delete_session(session)
+
+ return template_response
diff --git a/ecs/jskult-webapp/src/main.py b/ecs/jskult-webapp/src/main.py
index 174f097a..f0e1807d 100644
--- a/ecs/jskult-webapp/src/main.py
+++ b/ecs/jskult-webapp/src/main.py
@@ -10,8 +10,9 @@ 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()
+app = FastAPI(openapi_url=None)
# 静的ファイルをマウント
app.mount('/static', StaticFiles(directory=path.dirname(static.__file__)), name='static')
@@ -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())
diff --git a/ecs/jskult-webapp/src/middleware/__init__.py b/ecs/jskult-webapp/src/middleware/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/ecs/jskult-webapp/src/middleware/middleware.py b/ecs/jskult-webapp/src/middleware/middleware.py
new file mode 100644
index 00000000..264fd190
--- /dev/null
+++ b/ecs/jskult-webapp/src/middleware/middleware.py
@@ -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
diff --git a/ecs/jskult-webapp/src/router/session_router.py b/ecs/jskult-webapp/src/router/session_router.py
index 9389a722..30690590 100644
--- a/ecs/jskult-webapp/src/router/session_router.py
+++ b/ecs/jskult-webapp/src/router/session_router.py
@@ -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:
@@ -123,7 +124,6 @@ class AfterSetCookieSessionRoute(MeDaCaRoute):
response.set_cookie(
key='session',
value=session_key,
- max_age=environment.SESSION_EXPIRE_MINUTE * 60, # cookieの有効期限は秒数指定なので、60秒をかける
secure=True,
httponly=True
)
diff --git a/ecs/jskult-webapp/src/services/session_service.py b/ecs/jskult-webapp/src/services/session_service.py
index 657e648d..b58d7241 100644
--- a/ecs/jskult-webapp/src/services/session_service.py
+++ b/ecs/jskult-webapp/src/services/session_service.py
@@ -1,19 +1,26 @@
-
-from src.logging.get_logger import get_logger
-from src.model.internal.session import UserSession
-
-logger = get_logger('セッション管理')
-
-
-def set_session(session: UserSession) -> str:
- session.save()
- return session.session_key
-
-
-def get_session(key: str) -> UserSession:
- try:
- session = UserSession.get(hash_key=key, consistent_read=True)
- return session
- except UserSession.DoesNotExist as e:
- logger.debug(f'セッション取得失敗:{e}')
- return None
+
+from src.logging.get_logger import get_logger
+from src.model.internal.session import UserSession
+
+logger = get_logger('セッション管理')
+
+
+def set_session(session: UserSession) -> str:
+ session.save()
+ return session.session_key
+
+
+def get_session(key: str) -> UserSession:
+ try:
+ session = UserSession.get(hash_key=key, consistent_read=True)
+ return session
+ except UserSession.DoesNotExist as e:
+ logger.debug(f'セッション取得失敗:{e}')
+ return None
+
+def delete_session (session: UserSession):
+ try:
+ session.delete()
+ return
+ except:
+ return
\ No newline at end of file
diff --git a/ecs/jskult-webapp/src/templates/_header.html b/ecs/jskult-webapp/src/templates/_header.html
index a0e90606..95ac2464 100644
--- a/ecs/jskult-webapp/src/templates/_header.html
+++ b/ecs/jskult-webapp/src/templates/_header.html
@@ -1,19 +1,19 @@
-
+
+
{{subtitle}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ecs/jskult-webapp/src/templates/logout.html b/ecs/jskult-webapp/src/templates/logout.html
index ed17c630..cc79a4ac 100644
--- a/ecs/jskult-webapp/src/templates/logout.html
+++ b/ecs/jskult-webapp/src/templates/logout.html
@@ -15,61 +15,8 @@
{{logout.reason}}
{% endautoescape %}
-
{{logout.link_text}}
-
-
-
-