如何在AWS上实施HIPAA规定的技术安全措施【完整手册】
在我还从未听说过“HIPAA审计”这个术语之前,我花了三天的时间帮助一家医疗保健领域的SaaS初创企业修复一个配置错误的S3存储桶。其实并没有发生任何数据泄露事件——没有人访问过那些数据。但那个存储桶是公开可查询的,其中存放着患者的预约记录,因此周五晚上11点时,这家企业的首席执行官收到了一位安全研究人员的消息。 最终并没有被处以罚款,但却产生了大量的法律费用,还有进行整改所需的成本。在接下来的六个月里,与企业客户进行的那些涉及HIPAA合规性的沟通也确实给企业带来了不少麻烦。 HIPAA并不只是一项抽象的合规要求,而是一系列具体的技术规范,这些规范会直接影响到基础设施的建设决策。如果这些规定
目录
您将学到的内容
HIPAA规定的五项技术安全要求,以及每一项要求具体涉及哪些AWS基础设施配置
如何使用可实际应用的代码来实现唯一用户身份识别和自动登出功能
- 如何使用AWS KMS为电子健康信息字段实现加密保护
- 如何自动执行HIPAA合规性检查,并保持系统始终处于可审计状态
- 审计人员会针对每一项安全要求要求提供哪些具体证据
如何利用哈希链技术和S3对象锁机制来构建不可篡改的审计日志
哪些TLS配置符合HIPAA对传输安全的要求
满足设施访问控制要求的完整VPC架构设计
让我们认真地来完成这项工作吧。
先决条件
在开始遵循本指南之前,您需要具备以下条件:
知识要求:
具有中级水平的AWS使用经验——您已经能够在EC2或ECS上部署应用程序,熟悉RDS的使用,并了解VPC及IAM角色的相关概念
能够熟练阅读Python代码以及Terraform HCL配置文件
对加密技术有基本的了解——至少在概念层面上知道对称加密、非对称加密和哈希函数的含义
法律要求——必须先签署业务合作伙伴协议:在编写任何与HIPAA相关的基础设施代码之前,您的组织必须与AWS签订正式的业务合作伙伴协议。您可以通过AWS Artifact控制台来接受该协议。如果没有签署此类协议,即使您的技术控制措施设计得非常完善,使用AWS处理电子健康信息也不符合HIPAA的规定。
所需工具:
Terraform 1.5或更高版本
已配置好的AWS CLI v2
安装了
boto3、cryptography和pyjwt的Python 3.10或更高版本
重要说明:本指南涵盖了45 CFR §164.312中规定的技术保障措施。不过,要符合HIPAA的要求,还需要采取管理保障措施(§164.308)和物理保障措施(§164.310)。本指南中介绍的技术控制措施虽然是必要的,但还不够充分——这些措施必须与书面政策、员工培训以及正式的风险评估相结合才能达到合规要求。
第1部分:了解HIPAA的技术保障措施
1.1 这五项保障措施具体要求什么
HIPAA的安全规则规定了五类技术保障措施,每一类措施都对应着具体的技术实现方案:
| 保障措施 | 相关法规条款 | >具体要求 | >AWS的实现方式 |
|---|---|---|---|
| 访问控制 | §164.312(a)(1) | 使用唯一的用户身份进行访问控制,提供紧急访问机制,实施自动登出功能,并对数据进行处理加密 | 通过IAM、Cognito、KMS等服务来实现访问控制 |
| 审计监控 | §164.312(b) | 记录并审查所有与电子健康信息相关的访问活动 | 利用CloudTrail、CloudWatch、Kinesis及S3对象锁定功能来实现监控 |
| 数据完整性保护 | §164.312(c)(1) | 防止数据被非法篡改或删除 | 通过哈希链技术、数字签名以及数据删除保护机制来确保数据完整性 |
| 传输安全保障 | §164.312(e)(1) | 在数据传输过程中对电子健康信息进行加密处理 | 使用TLS 1.2+、mtLS协议,以及API Gateway和ALB策略来确保传输安全 |
| 设施访问控制 | §164.310(a)(1) | 限制对物理设备和逻辑系统的访问权限 | 通过VPC架构、安全组及NACL规则来实现访问控制 |
1.2 合规性证据的区分标准
在实际应用HIPAA规范时,最重要的概念是:合规性与合规性的证明其实是两回事,而审计人员关注的是这两点。
合规性意味着你的加密设置是正确的;而合规性的证明则包括:拥有记录KMS密钥更替过程的CloudTrail日志、每个RDS实例上显示“StorageEncrypted: true”的命令输出结果,以及在需要时能够提供的配置截图。
本指南的每一部分都会提供具体的证据收集指令。请逐一执行这些指令,并保存相应的输出文件。给这些文件起名时,应注明文件生成的日期以及它们所证明的具体内容。当审计人员询问“你能证明你的RDS实例在数据存储阶段是经过加密处理的吗?”时,你只需将准备好的文件交给他们,而无需在现场重新运行命令。
1.3 保护性健康信息——明确自己的责任范围
要遵守HIPAA规范,首先必须清楚系统中哪些数据属于ePHI。需要被保护的18种HIPAA标识符如下:
# phi_classifier.py
# 参考:18种HIPAA标识符
HIPAA_IDENTIFIERS = [
'full_name', 'first_name', 'last_name',
'geographic_subdivision', # 任何小于州的行政区划
'date_of_birth', 'admission_date', 'discharge_date', 'death_date',
'phone_number', 'fax_number', 'email_address',
'social_security_number', 'medical_record_number',
'health_plan_beneficiary_number', 'account_number',
'certificate_license_number', 'vehicle_identifier',
'device_identifier', 'web_url', 'ip_address',
'biometric_identifier', 'full_face_photo',
]
# 任何包含这些标识符中的一种或多种,并且与健康信息结合在一起的数据记录,都属于ePHI,因此都受HIPAA规范的约束。
第2部分:访问控制——§164.312(a)(1)
§164.312(a)(1)规定了四项具体的实施要求:唯一用户标识、紧急访问流程、自动登出机制,以及加密和解密功能。
2.1 唯一用户标识
这一要求意味着:任何能够访问ePHI的用户都必须拥有唯一的标识符。共享账户显然违反了这一规定。在实际操作中,这一点非常重要:当发生审计或安全事件时,调查人员需要准确了解是谁在何时访问了哪些患者记录。如果三名护士共用一个登录账号,那么所有访问记录就会消失。而为每个用户分配唯一的标识符,就能确保每一次对ePHI的访问都能被追溯到具体的个人——这正是HIPAA规范所要求的,也是法律团队在出现问题时所需要的证据。
下面的代码通过在用户创建账户时为其分配一个UUID v4来实现这一要求。这个随机生成的标识符在整个系统中都是唯一的,即使用户的账户被删除后,该标识符也不会被重新使用。当用户被移除时,其账户只会被“软删除”:UUID仍然会保存在数据库和所有的审计日志中,因此你可以随时追踪到是谁访问了哪些数据。此外,这种认证方式默认会强制使用多因素认证,并会记录失败的登录尝试次数;在连续五次登录失败后,系统会自动暂停相关账户,从而满足HIPAA对访问控制和账户管理的要求。
# user_identity_service.py
# 遵循HIPAA标准的用户身份管理机制
import uuid
import hashlib
import hmac
import os
from datetime import datetime, timezone
from dataclasses import dataclass
from typing import Optional
@dataclass
class HIPAAUser:
user_id: str # UUID——永不重复使用
email: str
role: str # 临床人员/管理人员/工程技术人员
department: str
status: str # 活跃/暂停/已删除
mfaenabled: bool
created_at: str
last_login: Optional[str] = None
failed_attempts: int = 0
class UserIdentityService:
"""
实现HIPAA §164.312(a)(1)(i)标准——唯一用户身份识别机制。
主要特性:
- 每位用户都会获得一个唯一的UUID v4,该UUID不会被重复使用
- 账户会被进行“软删除”——即使用户离开系统,其UUID也会被永久保存在审计日志中
- 所有的认证操作都会被记录下来,其中会包含user_id、时间戳以及认证结果等信息
"""
MAX_FAILED_ATTEMPTS = 5
def __init__(self, db, audit_logger):
self.db = db
self.audit = audit_logger
def create_user(self, email: str, role: str, department: str,
created_by: str) -> HIPAAUser:
"""创建一个具有唯一标识符的用户。"""
user = HIPAAUser(
user_id=str(uuid.uuid4()),
email=email.strip().lower(),
role=role,
department=department,
status='ACTIVE',
mfaenabled=True,
created_at.datetime.now(timezone.utc).isoformat(),
)
self.db.save_user(user)
self.audit.log(
event_type='USER CREATED',
actor_id=created_by,
subject_id=user.user_id,
details={'role': role, 'department': department}
)
return user
def authenticate(self, email: str, password: str, mfa_token: str) -> Optional[str]:
"""对用户进行身份验证。如果验证成功,会返回一个JWT访问令牌。"""
user = self.db.find_user_by_email(email.strip().lower())
if not user:
self.audit.log(
event_type='AUTH_FAILURE',
actor_id=None,
subject_id=None,
details={'reason': 'unknown_email',
'email_hash': hashlib.sha256(email.encode()).hexdigest()[:16]}
)
return None
if user.status != 'ACTIVE':
self.audit.log(
event_type='AUTH_FAILURE',
actor_id=user.user_id,
subject_id=user.user_id,
details {'reason': f'account_{user.status.lower()}'}
)
return None
if not self._verify_password(password, self.db.get_password_hash(user.user_id)):
user.failed_attempts += 1
if user_FAILED_attempts >= self.MAX_FAILED_ATTEMPTS:
user.status = 'SUSPENDED'
self.audit.log(
event_type='ACCOUNT SUSPENDED',
actor_id='system',
subject_id=user.user_id,
details {'reason': 'max_failed_attempts',
'attempts': user.failed_attempts}
)
self.db.save_user(user)
return None
user_FAILED_attempts = 0
user.last_login = datetime.now(timezone.utc).isoformat()
self.db.save_user(user)
token = self._issue_jwt(user)
self.audit.log(
event_type='AUTH_SUCCESS',
actor_id=user.user_id,
subject_id=user.user_id,
details {'token_jti': self._extract_jti(token)}
)
return token
@staticmethod
def _verify_password(plaintext: str, stored_hash: str) -> bool:
candidate = hashlib.pbkdf2_hmac(
'sha256', plaintext.encode(), b'salt', 100_000
).hex()
return hmac.compare_digest(candidate, stored_hash)
def _issue_jwt(self, user: HIPAAUser) -> str:
import jwt
return jwt.encode(
{
'sub': user.user_id,
'role': user.role,
'jti': str(uuid.uuid4()),
'exp': int(datetime.now(timezone.utc).timestamp()) + 900,
'iat': int(datetime.now(timezone.utc).timestamp()),
},
os.environ['JWT_PRIVATE_KEY'],
algorithm='RS256'
)
@staticmethod
def _extract_jti(token: str) -> str:
import jwt
return jwt.decode(token, options={'verify_signature': False})['jti']
以下两个证据可以向审计人员证明您的系统确实满足了唯一性要求:第一个证据表明数据库中的每个user_id都是唯一的(不存在重复项,也没有用户共享凭证);第二个证据则说明所有活跃用户都启用了多因素认证功能——这两点正是审计人员在审查相关条款时会重点检查的内容。
为审计人员提供的证据 —— §164.312(a)(1)(i):
# 证明不存在共享账户
psql $DATABASE_URL -c "
SELECT COUNT(*) AS total_users,
COUNT(DISTINCT user_id) AS unique_ids,
COUNT(CASE WHEN status='ACTIVE' THEN 1 END) AS active_users
FROM hipaa_users;
"
# 证明所有活跃用户都启用了多因素认证功能 —— 预期结果:0
psql $DATABASE_URL -c "
SELECT COUNT(*) FROM hipaa_users
WHERE status = 'ACTIVE' AND mfa_enabled = FALSE;
"
2.2 自动登出 —— §164.312(a)(2)(iii)
这项要求是指:必须实施相应的电子程序,以便在用户长时间处于不活动状态后自动终止其电子会话。大多数医疗应用都将这个时间限制设置为15分钟。
设置这一控制机制的原因非常简单:在临床环境中,医护人员通常会共享工作站。例如,一名护士登录系统查看患者信息,然后被叫走,但并未关闭浏览器。如果没有自动登出功能,下一个使用该工作站的人就可以利用他人的凭证直接访问电子健康信息。这种控制措施的目的并不是为了防范恶意行为,而是为了适应医疗工作的实际运作方式。
下面的代码使用了有效期较短的JWT令牌来实现自动登出功能。当用户完成身份验证后,他们会获得一个令牌,而这个令牌在用户15分钟没有进行任何操作后就会失效。每次调用API时,系统都会通过生成新的令牌来重置这个时间限制。此外,系统还设置了8小时的绝对会话时长上限:无论用户是否进行任何操作,超过8小时后都必须重新进行身份验证。这样就可以防止用户只是将某个标签页留在后台运行而导致会话无限期地保持开放状态。validate_session装饰器被应用于所有涉及处理电子健康信息的接口,因此没有任何访问路径能够绕过这些检查。
# session_manager.py
# 实现§164.312(a)(2)(iii)——自动登出功能
import os
import uuid
from datetime import datetime, timezone
from functools import wraps
from flask import request, g, jsonify
import jwt
INACTIVITY_TIMEOUT_SECONDS = 15 * 60 # 15分钟
ABSOLUTE_SESSION_seconds = 8 * 60 * 60 # 最长8小时
def validate_session(f):
"""用于保护处理电子健康信息接口的装饰器."""
@wraps(f)
def decorated(*args, **kwargs):
auth_header = request.headers.get('Authorization', '')
if not auth_header.startswith('Bearer '):
return jsonify({'error': 'MISSING_TOKEN'}), 401
token = auth_header[7:]
try:
payload = jwt.decode(
token,
os.environ['JWT_PUBLIC_KEY'],
algorithms=['RS256']
)
except jwt.ExpiredSignatureError:
return jsonify({
'error': 'SESSION_EXPIRED',
'message': '您的会话已因长时间不活动而失效。请重新登录。",
'code': 'INACTIVITY_TIMEOUT'
}), 401
except jwt.InvalidTokenError as e:
return jsonify({'error': 'INVALID_TOKEN', 'detail': str(e)}), 401
# 检查会话的有效时长
issued_at = payload.get('session_start', payload['iat'])
session_age = datetime.now(timezone.utc).timestamp() - issued_at
if session_age > ABSOLUTE_SESSION_seconds:
return jsonify({
'error': 'SESSION_EXPIRED',
'message': '您的会话已超过8小时的有效期限。请重新登录。",
'code': 'ABSOLUTE_TIMEOUT'
}), 401
g.user_id = payload['sub']
g.role = payload.get('role')
g.jti = payload.get('jti')
return f(*args, **kwargs)
return decorated
def issue_access_token(user_id: str, role: str, session_start: int = None) -> str:
"""生成一个有效期为15分钟、绝对会话时长为8小时的访问令牌。」
now = int(datetime.now(timezone.utc).timestamp())
return jwt.encode(
{
'sub': user_id,
'role': role,
'jti': str(uuid.uuid4()),
'iat': now,
'exp': now + INACTIVITY_TIMEOUT_seconds,
'session_start': session_start or now,
},
os.environ['JWT_PRIVATE_KEY'],
algorithm='RS256'
)
2.3 静态数据加密 — §164.312(a)(2)(iv)
要求是:必须实施一种机制来对电子健康信息进行加密和解密。
静态数据加密的含义是:如果有人获得了对您的存储介质的物理访问权限——无论是硬盘、备份磁带还是S3对象——没有加密密钥,他们就无法读取其中的数据。在AWS平台上,这种保护措施分为两层。第一层是存储级别的加密,数据库或存储服务会自动将写入磁盘的每一字节数据都进行加密;第二层则是更强大的字段级别加密,应用程序会在数据被传递给数据库之前就对其中的敏感信息进行加密——因此,即使数据库管理员拥有完整的SQL访问权限,如果没有相应的应用密钥,他们也无法读取患者的社会安全号码或诊断结果。
第一层:存储加密(使用Terraform实现):
# rds_hipaa.tf — 使用客户管理的KMS密钥的符合HIPAA标准的RDS实例
resource "aws_kms_key" "rds" {
description = "用于RDS电子健康信息加密的客户管理KMS密钥"
enable_key_rotation = true
deletion_window_in_days = 30
tags = {
Purpose = "HIPAA-ePHI-encryption"
Environment = "production"
Control = "164.312(a)(2)(iv)"
}
}
resource "aws_db_instance" "hipaa_postgres" {
identifier = "hipaa-production-db"
engine = "postgres"
engine_version = "15.4"
instance_class = "db.r7g.large"
storage_encrypted = true
kms_key_id = aws_kms_key.rds.arn
backup_retention_period = 30
deletion_protection = true
skip_final_snapshot = false
final_snapshot_identifier = "hipaa-production-db-final-snapshot"
db_subnet_group_name = aws_db_subnet_group.hipaa.name
vpc_security_group_ids = [aws.security_group.rds.id]
publiclyaccessible = false
tags = {
DataClassification = "ePHI"
HIPAAControl = "164.312(a)(2)(iv)"
}
}
第二层:针对最高敏感度数据的字段级别应用加密:
存储加密可以在数据介质被盗的情况下保护您的数据;而字段级别加密则能防止那些虽然拥有合法数据库访问权限但不应该能够读取原始患者数据的使用者窃取这些信息。下面的FieldEncryption代码实现了封装式加密:AWS KMS会为每个字段值生成一个唯一的密钥,用这个密钥来加密明文数据,而只有加密后的密钥才会被存储下来。即使攻击者获取了您的整个数据库,想要解密其中的数据也需要分别调用KMS接口——而这些操作都会被记录在日志中,并且会受到速率限制;此外,解密这些数据还需要具备正确的IAM权限。加密机制能够确保每条加密信息都与它的用途和所有者相对应,因此,为某个患者的记录解密的密钥不能被用于其他患者的数据。
# field_encryption.py
# 使用AWS KMS进行包封加密
import boto3
import base64
from cryptography.fernet import Fernet
from typing import Optional
kms = boto3.client('kms')
KMS_KEY = 'alias/hipaa-rds-ephi'
class FieldEncryption:
"""
用于ePHI字段的包封加密机制。
工作原理:
1. AWS KMS生成一个数据密钥(明文 + 加密后的副本)
2. 使用Fernet(AES-128-CBC)算法,用该数据密钥对字段值进行加密
3> 只有加密后的数据密钥会与密文一起被存储
4> 解密时:首先由KMS解密存储的数据密钥,然后再用Fernet解密字段值
5> 具有直接SQL访问权限的数据库管理员只能看到经过base64编码的密文
def encrypt(self, plaintext: str, context: dict) -> Optional[dict]:
if not plaintext:
return None
data_key = kms.generate_data_key(
KeyId=KMS_KEY,
KeySpec='AES_256',
EncryptionContext=context
)
fernet = Fernet(data_key['Plaintext'])
ciphertext = fernet.encrypt(plaintext.encode('utf-8'))
return {
'ciphertext': base64.b64encode(ciphertext).decode(),
'encrypted_data_key': base64.b64encode(data_key['CiphertextBlob']).decode(),
'encryption_context': context,
}
def decrypt(self, payload: dict) -> Optional[str]:
if not payload:
return None
decrypted_key = kms.decrypt(
CiphertextBlob=base64.b64decode(payload['encrypted_data_key]),
EncryptionContext=payload['encryption_context']
)
fernet = Fernet(decrypted_key['Plaintext'])
plaintext = fernet.decrypt(base64.b64decode(payload['ciphertext']))
return plaintext.decode('utf-8')
为审计人员提供的证据 — §164.312(a)(2)(iv):
# 验证RDS存储加密情况
aws rds describe-db-instances \
--db-instance-identifier hipaa-production-db \
--query 'DBInstances[0].{Encrypted:StorageEncrypted,KMSKey:KmsKeyId}' \
--output table
# 验证是否启用了KMS密钥轮换 — 预期结果:true
aws kms get-key-rotation-status \
--key-id alias/hipaa-rds-ephi \
--query 'KeyRotationEnabled'
第三部分:审计控制措施 — §164.312(b)
§164.312(b)要求实施硬件、软件及程序机制,以记录并审查那些包含或使用ePHI的信息系统中的所有操作。无论是访问、修改还是删除操作,都必须被记录下来,并且这些记录必须是不可篡改的,同时需保留六年。
3.1 审计日志格式
任何与ePHI相关的操作都必须回答五个问题:是谁执行的操作、具体做了什么、何时进行的操作、针对的是哪条记录,以及操作是从哪里发起的。
下面的log_ephi_event函数是您应用程序中的核心审计机制。每当用户读取、更新或删除患者记录时,在返回响应之前都会调用这个函数。该函数会记录执行操作的用户的身份信息、IP地址、使用的浏览器以及会话ID,同时还会记录具体的操作内容、涉及的资源以及操作时间戳——此外,还会添加一个更为强大的安全措施:加密链。每条日志记录都会包含前一条记录的SHA-256哈希值。这意味着,如果有人试图篡改某条日志记录,哪怕只是修改一个字符,整个加密链中的后续哈希值都会失效,从而使得篡改行为能够被立即发现。这些日志记录会被发送到Kinesis,然后再由Kinesis分发到S3进行长期存储。有一条非常重要的规则:details字典中绝对不能包含ePHI的实际数值,而只能包含字段名称。例如,应该记录某个SSN字段被访问过,但不要记录SSN的具体数值。
# audit_logger.py
# 实现§164.312(b) — 审计控制措施
import hashlib
import json
import uuid
import boto3
from datetime import datetime, timezone
from typing import Any, Optional
kinesis = boto3.client('kinesis')
STREAM = 'hipaa-audit-events'
_last_hash = '0' * 64 # 连接字符串以64个零开头
def log_ephi_event(
event_type: str,
actor_id: Optional[str],
patient_id: Optional[str],
resource: Optional[str],
action: str,
details: dict,
request_ctx: dict = None,
) -> str:
"""
记录与HIPAA相关的事件。
在details字典中不要包含任何个人健康信息——只需填写字段名称即可。
"""
global _last_hash
entry = {
'actor_id': actor_id,
'actor_ip': (request_ctx or {}).get('ip'),
'actor_ua': (request_ctx or__).get('user_agent'),
'session_id': (request_ctx or?).get('session_id'),
'event_type': event_type,
'action': action,
'resource': resource,
'details': details,
'patient_id': patient_id,
'timestamp': datetime.now(timezone.utc).isoformat(),
'service': 'healthcare-api',
'environment': 'production',
'previous_hash': _last_hash,
'log_id': str(uuid.uuid4()),
}
canonical = json.dumps(entry, sort_keys=True)
entry_hash = hashlib.sha256(canonical.encode()).hexdigest()
entry['log_hash'] = entry_hash
_last_hash = entry_hash
kinesis.put_record(
StreamName=STREAM,
Data=json.dumps(entry),
PartitionKey=actor_id or 'system'
)
return entry_hash
def log_phi_read(actor_id: str, patient_id: str, resource: str,
purpose: str, request_ctx: dict = None):
return log_ephi_event(
event_type='PHI_ACCESS',
actor_id=actor_id,
patient_id=patient_id,
resource=resource,
action='READ',
details={'purpose': purpose},
request_ctx=requestctx,
)
def log_phi_update(actor_id: str, patient_id: str, resource: str,
fields_changed: list, request_ctx: dict = None):
return log_ephi_event(
event_type='PHI_UPDATE',
actor_id=actor_id,
patient_id=patient_id,
resource=resource,
action='UPDATE',
details={'fields_changed': fields_changed}, # 只填写字段名称,不要包含实际值
request_ctx=requestctx,
)
3.2 使用S3对象锁实现不可变的日志存储
仅仅将日志保存到S3是不够的——因为标准S3存储桶中的日志可能会被删除,这样违规者就可以在发生数据泄露后掩盖自己的痕迹。而处于“合规模式”的S3对象锁能够解决这个问题:它会使存储桶中的所有对象在您指定的保留期内永久保持不可修改的状态。在合规模式下,即使是AWS的root账户也无法在保留期届满之前删除这些对象。下述存储桶被配置为2,190天(即六年)的保留期限,这完全符合HIPAA对于文档保留的要求。同时,该存储桶也支持版本控制功能,因此即使有写入操作部分覆盖了原有数据,原始版本也会被保留下来。
# audit_log_bucket.tf
# 一个启用了对象锁功能的S3存储桶,且该功能处于合规模式
# 任何人都无法删除或修改这些日志——包括root用户
resource "aws_s3_bucket" "audit_logs" {
bucket = "hipaa-audit-logs-${data.aws_caller_identity.current.account_id}"
object_lockenabled = true
tags = {
DataClassification = "audit-log"
HIPAAControl = "164.312(b)"
RetentionYears = "6"
}
}
resource "aws_s3_bucket_versioning" "audit_logs" {
bucket = aws_s3_bucket.audit_logs.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_object_lockConfiguration" "audit_logs" {
bucket = aws_s3_bucket.audit_logs.id
rule {
default_retention {
mode = "COMPLIANCE" # 任何人都无法删除这些日志——甚至root用户也不行
days = 2190 # 6年等于2,190天
}
}
}
resource "aws_s3_bucket_public_access_block" "audit_logs" {
bucket = aws_s3_bucket.audit_logs.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
为审计人员提供的证据——§164.312(b):
# 验证对象锁功能是否处于合规模式
aws s3api get-object-lock-configuration \
--bucket hipaa-audit-logs-YOUR ACCOUNT_ID \
--query 'ObjectLockConfiguration'
# 预期结果:Mode=COMPLIANCE, Days=2190
第4部分:完整性控制——§164.312(c)(1)
§164.312(c)(1)要求制定相应的政策和程序,以保护电子健康信息不被非法修改或销毁。
完整性控制能够解决一个具体问题:如何确认患者记录在创建后没有被篡改过?存储加密可以防止未经授权的第三方读取数据,但无法阻止经过授权的用户(如数据库管理员或内部账户被入侵者控制)悄悄修改记录。而数字签名则可以做到这一点。当创建一条记录时,sign_record函数会使用KMS的非对称密钥生成一个加密签名,并将这个签名与记录一起存储起来。verify_record函数可以在任何时候验证记录的内容是否与签名完全匹配——任何修改,哪怕只是单个字符,都会产生不同的签名,从而导致验证失败。每周进行的完整性扫描会针对指定表中的每条电子健康信息记录调用verify_record函数,如果有任何记录无法通过验证,系统就会发送SNS警报,从而形成一种持续的防篡改机制。
# integrity_service.py
# 使用KMS非对称密钥为每条电子健康信息记录生成数字签名
import boto3
import base64
import json
from datetime import datetime, timezone
kms = boto3.client('kms')
SIGNING_KEY = 'alias/hipaa-record-signing'
def sign_record(record: dict) -> str:
"""为患者记录生成数字签名,并将签名与记录一起存储。"""
canonical = json.dumps(record, sort_keys=True)
response = kms.sign(
KeyId=SIGNING_KEY,
Message=canonical.encode(),
MessageType='RAW',
SigningAlgorithm='RSASSA_PKCS1_V1_5_SHA_256'
)
return base64.b64encode(response['Signature']).decode()
def verify_record(record: dict, signature: str) -> bool:
"""验证患者记录自签名以来是否被修改过。"""
canonical = json.dumps(record, sort_keys=True)
try:
kms.verify(
KeyId=SIGNING_KEY,
Message=canonical.encode(),
MessageType='RAW',
Signature=base64.b64decode(signature),
SigningAlgorithm='RSASSA_PKCS1_V1_5_SHA_256'
)
return True
except kms.exceptions.KMSInvalidSignatureException:
return False
def weekly_integrity_scan(db, table_name: str) -> dict:
"""
定期执行的任务:验证每条电子健康信息记录的数字签名。
任何无法通过验证的记录都会被标记为可能已被篡改。
根据§164.312(c)(1)的规定,这项检查需要每周进行一次。
"""
total = 0
failures = []
for record_id, record, signature in db.iterate_records_with_signatures(table_name):
total += 1
if not verify_record(record, signature):
failures.append({
'record_id': record_id,
'table': table_name,
'detected_at': datetime.now(timezone.utc).isoformat(),
})
result = {
'scan_date': datetime.now(timezone.utc).isoformat(),
'table': table_name,
'records_checked': total,
'integrity_failures': len(failures),
'failed_records': failures,
}
if failures:
sns = boto3.client('sns')
sns.publish(
TopicArn='arn:aws:sns:us-east-1:YOUR ACCOUNT:hipaa-integrity-alerts',
Subject=f'INTEGRITY FAILURE: {len(failures)} records in {table_name)',
Message=json.dumps(result, indent=2)
)
return result
第5部分:传输安全——§164.312(e)(1)
§164.312(e)(1)要求在数据传输过程中采取技术性安全措施,以防止未经授权的访问,从而保护电子健康信息的安全。最低要求的TLS版本为TLS 1.2,推荐使用TLS 1.3。SSL、TLS 1.0和TLS 1.1均不符合要求。
应用负载均衡器的SSL配置(Terraform示例):
作为所有外部流量进入您的HIPAA应用的入口点,负载均衡器是执行TLS安全要求的第一个环节。下面的Terraform代码配置了HTTPS监听器,使用了ELBSecurityPolicy-TLS13-1-2-2021-06策略——这是AWS提供的配置方案,该方案允许TLS 1.2和TLS 1.3连接,同时拒绝所有更旧的协议及弱加密套件。HTTP监听器也被单独配置为将所有80端口的流量永久重定向到443端口,从而确保即使客户端不小心使用HTTP协议进行连接,也不会有电子健康信息以未加密的形式被传输。
# alb_hipaa.tf
resource "aws_alblistener" "hipaa_https" {
load_balancer_arn = aws_alb.hipaa.arn
port = 443
protocol = "HTTPS"
ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06"
certificate_arn = aws_acm_certificate.hipaa.arn
default_action {
type = "forward"
target_group_arn = aws_alb_target_group.hipaa_api.arn
}
}
# 将所有HTTP流量重定向到HTTPS
resource "aws_alblistener" "hipaa_http_redirect" {
load_balancer_arn = aws_alb.hipaa.arn
port = 80
protocol = "HTTP"
default_action {
type = "redirect"
redirect {
port = "443"
protocol = "HTTPS"
status_code = "HTTP_301"
}
}
}
直接部署时使用的nginx TLS配置:
如果您的应用服务器直接处理TLS连接的终止过程,而不是将这一任务交给负载均衡器来处理,那么下面的nginx配置将在服务器层面强制执行相同的安全标准。ssl_protocols指令明确指定了仅允许TLSv1.2和TLSv1.3协议,因此nginx会拒绝任何使用较旧协议的连接请求。ssl_ciphers列表中只列出了基于ECDHE的加密套件,并且这些套件都使用了AES-GCM或ChaCha20-Poly1305算法,这样的配置能够确保通信的安全性——即使私钥后来被泄露,也无法解密之前的会话数据。Strict-Transport-Security头部的设置使得浏览器在用户输入HTTP地址时也会自动使用HTTPS协议。ssl_session_tickets off这一设置可以有效防止某些攻击手段,因为这些攻击手段可能会利用会话票证钥匙来解密过去的会话数据。
# /etc/nginx/conf.d/hipaa-tls.conf
server {
listen 443 ssl http2;
server_name api.your-healthcare-app.com;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
ssl_prefer_server_ciphers off;
# HTTP严格传输安全设置——有效期为2年
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
ssl_stapling on;
ssl_stapling_verify on;
ssl_session_tickets off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
}
为审计人员提供的证据 — §164.312(e)(1):
# 验证TLS 1.1是否被拒绝
openssl s_client \
-connect api.your-healthcare-app.com:443 \
-tls1_1 2>&1 | grep -E "CONNECTED|handshake failure"
# 预期结果:握手失败
# 验证TLS 1.2是否成功
openssl s_client \
-connect api.your-healthcare-app.com:443 \
-tls1_2 2>&1 | grep "CONNECTED"
# 检查ALB的SSL策略
aws elbv2 describe-listeners \
--load-balancer-arn YOUR_ALB_ARN \
--query 'Listeners[*].{Port:Port,SslPolicy:SslPolicy}' \
--output table
第6部分:HIPAA相关的AWS网络架构
在云环境中,§164.310(a)(1)中规定的“物理设施访问控制”实际上被理解为逻辑层面的网络访问控制——也就是通过VPC架构将处理敏感健康信息的系统与其他工作负载隔离开来。
下图所示的三层VPC结构实现了对敏感健康信息的网络隔离:公共子网中仅包含负载均衡器,没有任何用于处理或存储患者数据的服务器能够被公众直接访问;私有应用子网中部署着API服务器,这些服务器可以接收来自负载均衡器的请求,但本身没有直接的互联网接入路径;而私有数据子网中则安装了RDS和ElastiCache,这些服务也只能接收来自应用层安全组的流量,无法直接通过互联网或公共子网进行通信。这意味着,如果负载均衡器遭到攻击,也无法直接访问数据库,它只能与应用程序服务器进行交互,而应用程序服务器在将数据传输给数据库之前会先执行自身的身份验证流程。
为S3和KMS配置的VPC端点确保了与敏感健康信息相关的流量必须通过AWS的内部网络传输,而不能经过公共互联网。VPC Flow Logs能够记录所有被允许或被拒绝的网络流量,这些日志为审计工作提供了额外的网络层证据,从而补充了应用层审计日志的信息。
# vpc_hipaa.tf — 三层VPC架构配置
resource "aws_vpc" "hipaa" {
cidr_block = "10.0.0.0/16"
enabledns_hostnames = true
enable_dns_support = true
tags = {
Name = "hipaa-production-vpc"
DataClass = "ePHI"
HIPAAControl = "164.310(a)(1)"
}
}
# 公共子网 — 仅包含负载均衡器,不存储敏感健康信息
resource "aws_subnet" "public" {
count = 2
vpc_id = aws_vpc.hipaa.id
cidr_block = "10.0.${count.index + 1}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = {Name = "hipaa-public-${count.index + 1}", DataClass = "none"}
}
# 私有应用子网 — API服务器,没有直接的互联网接入
resource "aws_subnet" "private_app" {
count = 2
vpc_id = aws_vpc.hipaa.id
cidr_block = "10.0.${count.index + 10}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = {Name = "hipaa-private-app-${count.index + 1}", DataClass = "ePHI-processing"}
}
# 私有数据子网 — RDS和ElastiCache
resource "aws_subnet" "private_data" {
count = 2
vpc_id = aws_vpc.hipaa.id
cidr_block = "10.0.${count.index + 20}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = {Name = "hipaa-private-data-${count.index + 1}", DataClass = "ePHI-storage"}
}
# VPC端点 — 这些AWS服务不需要通过公共互联网进行访问
# 即使在AWS内部,与敏感健康信息相关的流量也不得经过公共互联网
resource "aws_vpc_endpoint" "s3" {
vpc_id = aws_vpc.hipaa.id
service_name = "com.amazonaws.${var.region}.s3"
route_table_ids = [aws_route_table.private.id]
}
resource "aws_vpc_endpoint" "kms" {
vpc_id = aws_vpc.hipaa.id
service_name = "com.amazonaws.${var.region}.kms"
vpc_endpoint_type = "Interface"
subnet_ids = aws_subnet/private_app[*].id
security_group_ids = [aws_security_group.vpce.id]
private_dnsenabled = true
}
# 安全组 — 实现最小权限访问控制
resource "aws.security_group" "rds" {
name = "hipaa-rds-sg"
vpc_id = aws_vpc.hipaa.id
ingress {
from_port = 5432
to_port = 5432
protocol = "tcp"
security_groups = [aws_security_group.app.id]
description = "仅允许应用层访问RDS——禁止外部直接访问"
}
}
# VPC Flow Logs — 提供网络审计轨迹
resource "aws_flow_log" "hipaa" {
vpc_id = aws_vpc.hipaa.id
traffic_type = "ALL"
iam_role_arn = aws_iam_role.flow_logs.arn
log_destination = aws_cloudwatch_log_group.vpc_flow_logs.arn
tags = {HIPAAControl = "164.310(a)(1)", Retention = "365-days"}
}
第7部分:BAA涵盖的AWS服务
并非所有的AWS服务都受AWS业务合作伙伴协议的约束。如果使用非BAA协议覆盖的服务来处理电子健康信息,就会违反HIPAA法规。
| AWS服务 | 是否受BAA协议覆盖 | 备注 |
|---|---|---|
| EC2 | 是 | 创建EBS卷时需进行加密 |
| RDS(所有引擎类型) | 是 | 需要启用存储加密功能——此为可选设置 |
| S3 | 是 | 需要在桶策略中强制规定加密要求,并禁止公共访问 |
| Lambda | 是 | 环境变量中不得包含任何健康信息 |
| EKS | 是 | 需对etcd数据进行加密,并使用私有集群端点 |
| API Gateway | 是 | 需要启用CloudTrail日志记录功能 |
| KMS | 是 | 本指南中提到的所有加密操作均需使用KMS服务 |
| CloudTrail | 是 | 所有地区都需要启用该功能,并对日志进行加密处理 |
| CloudWatch Logs | 是 | 需要对日志组进行加密处理;这些日志中可能包含健康信息 |
| Kinesis Data Streams | 是 | 用于分发审计日志 |
| SNS | 是 | 需要对主题数据进行加密 |
| SQS | 是 | 需要对队列数据进行加密 |
| Secrets Manager | 是 | >推荐用于管理密码等敏感信息 |
那些默认不受BAA协议覆盖的服务,切勿用于处理电子健康信息:例如Amazon Connect(需要另行签订协议)、某些Amazon Comprehend Medical的功能模块(请查看当前的BAA协议内容),以及第三方市场平台上的产品。
第8部分:持续合规性监控
HIPAA合规性并不是一种一次性的达标状态,而是一种需要持续维护的状态。配置偏差是审计中导致违规问题的最常见原因之一:比如工程师创建了一个未启用加密功能的RDS实例,开发人员创建了一个允许公共访问的S3桶,或者某个日志组没有设置保留策略而不断积累数据……这些行为本身并无恶意,它们只是成长中的技术团队在日常工作中不可避免会遇到的问题。
下面介绍的这个监控工具被设计为每天自动运行的Lambda函数。它会检查你的AWS账户是否存在常见的HIPAA合规性违规情况,并将检测结果以结构化文件的形式保存到S3中。每条违规记录都会对应具体的法规条款,同时会标明其严重程度(严重或较高),并且会明确指出哪些资源存在合规性问题。通过每天运行这个监控工具,你可以在24小时内发现这些偏差,而不会等到审计时才发现问题。
# compliance_scanner.py
# 每日执行的Lambda任务——用于执行所有HIPAA合规性检查
import boto3
import json
from datetime import datetime, timezone
ec2 = boto3.client('ec2')
rds = boto3.client('rds')
s3 = boto3.client('s3')
ct = boto3.client('cloudtrail')
gd = boto3.client('guardduty')
def scan_all() -> dict:
"""执行所有HIPAA合规性检查,并返回检查结果。"""
findings = []
# §164.312(a)(2)(iv) — 检查:所有RDS实例是否已加密
for inst in rds.describe_db_instances['DBInstances']:
if not inst.get('StorageEncrypted'):
findings.append({
'control': '164.312(a)(2)(iv)',
'severity': 'CRITICAL',
'resource': inst['DBInstanceIdentifier'],
'finding': '该RDS实例未进行加密处理'
})
# §164.312(a)(2)(iv) — 检查:所有EBS卷是否已加密
for vol in ec2.describe_volumes['Volumes']:
if not vol.get('Encrypted'):
findings.append({
'control': '164.312(a)(2)(iv)',
'severity': 'HIGH',
'resource': vol['VolumeId'],
'finding': '该EBS卷未进行加密处理'
})
# §164.312(a)(2)(iv) — 检查:S3桶是否设置了公共访问限制
for bucket in s3.list_buckets['Buckets']:
name = bucket['Name']
try:
pab = s3.get_public_access_block(Bucket=name)[‘PublicAccessBlockConfiguration’]
if not all([pab.get('BlockPublicAcls'), pab.get('BlockPublicPolicy'),
pab.get('IgnorePublicAcls'), pab.get('RestrictPublicBuckets')]):
findings.append({
'control': '164.312(a)(2)(iv)',
'severity': 'CRITICAL',
'resource': f's3/{name}',
'finding': '该S3桶的公共访问限制设置未生效'
})
except s3.exceptions.NoSuchPublicAccessBlockConfiguration:
findings.append({
'control': '164.312(a)(2)(iv)',
'severity': 'CRITICAL',
'resource': f's3/{name}',
'finding': '该S3桶没有设置公共访问限制'
})
# §164.312(b) — 检查:CloudTrail是否已启用多区域功能
trails = ct.describe_trails['trailList']
multi_region = [t for t in trails if t.get('IsMultiRegionTrail')]
if not multi_region:
findings.append({
'control': '164.312(b)',
'severity': 'CRITICAL',
'resource': 'CloudTrail',
'finding': '未启用多区域功能——ePHI访问事件可能无法被记录'
})
# §164.312(b) — 检查:GuardDuty是否已启用
detectors = gd.list_detectors().get('DetectorIds', [])
if not detectors:
findings.append({
'control': '164.312(b)',
'severity': 'HIGH',
'resource': 'GuardDuty',
'finding': '未启用GuardDuty——威胁检测功能无法正常使用'
})
result = {
'scan_timestamp': datetime.now(timezone.utc).isoformat(),
'total_findings': len(findings),
'critical_findings': sum(1 for f in findings if f['severity'] == 'CRITICAL'),
'high_findings': sum(1 for f in findings if f['severity'] == 'HIGH'),
'findings': findings,
'compliant': len(findings) == 0,
}
# 将结果保存到S3中
evidence_s3 = boto3.client('s3')
date_str = datetime.now(timezone.utc).strftime('%Y/%m/%d')
evidence_s3.put_object(
Bucket='hipaa-compliance-evidence',
Key=f'scans/{date_str}/compliance_scan.json',
Body=json.dumps(result, indent=2),
ContentType='application/json',
)
return result
def lambda_handler(event, context):
result = scan_all()
print(f"扫描完成:共找到{result['total_findings']}项违规内容,合规情况为{result['compliant']}")
return result
第9部分:审计前的检查清单
在任何HIPAA审计开始前30天,运行此脚本。该脚本会针对您的AWS账户中的五个控制项进行检测,并将每个检查的结果保存到带有日期标签的证据文件目录中。每个文件的名称都对应其所检测的具体法规条款,因此当审核人员要求提供某项控制的证明时,您可以直接将相应的文件交给他们,而无需在现场重新执行命令。
以下是每项检查所收集的信息以及其输出结果的具体格式:
RDS加密检查会查询您账户中的所有数据库实例,并生成一份表格,其中列出现实例的标识符、存储加密是否已启用(true或false),以及使用的KMS密钥的ARN。对于符合HIPAA标准的账户来说,每行中都会显示StorageEncrypted: true这一字段。
KMS密钥轮换检查会查询所有别名为“hipaa”的密钥,并确认这些密钥的KeyRotationEnabled属性是否为true。如果发现有密钥的该属性值为false,那么这就会被视为违反§164.312(a)(2)(iv)条款的行为。
CloudTrail检查会返回日志记录的名称、是否支持多区域存储,以及这些日志是否使用了KMS密钥进行加密。这两个信息都是必需的。
ALB TLS检查会显示每个负载均衡器的监听端口、所使用的协议类型以及SSL策略的名称。审核人员会通过这些信息来确认是否已经禁用了过时的TLS版本。
VPC端点检查会列出您账户中的所有VPC端点,包括它们的服务名称、状态和类型。对于HIPAA合规账户来说,至少应该看到处于available状态的S3和KMS端点。
#!/usr/bin/env bash
# pre_audit_evidence_collector.sh
EVIDENCE_DIR="hipaa-evidence-$(date +%Y-%m-%d)"
mkdir -p "$EVIDENCE_DIR"
echo "正在收集HIPAA合规性相关证据..."
# §164.312(a)(2)(iv) — 静态数据加密
aws rds describe-db-instances \
--query 'DBInstances[*].{ID:DBInstanceIdentifier,Encrypted:StorageEncrypted,KMS:KmsKeyId}' \
--output table > "$EVIDENCE_DIR/164-312-a-2-iv-rds-encryption.txt"
aws kms list-aliases \
--query 'Aliases[?contains(AliasName,`hipaa`)].AliasName' \
--output text | xargs -I{} aws kms get-key-rotation-status --key-id {} \
>> "$EVIDENCE_DIR/164-312-a-2-iv-kms-rotation.txt"
# §164.312(b) — 审计控制措施
aws cloudtrail describe-trails \
--query 'trailList[*].{Name:Name,MultiRegion:IsMultiRegionTrail,Encrypted:KMSKeyId}' \
--output table > "$EVIDENCE_DIR/164-312-b-cloudtrail-config.txt"
# §164.312(e)(1) — 数据传输安全
aws elbv2 describe-listeners \
--load-balancer-arn $(aws elbv2 describe-load-balancers \
--query 'LoadBalancers[0].LoadBalancerArn' --output text) \
--query 'Listeners[*].{Port:Port,Protocol:Protocol,SslPolicy:SslPolicy}' \
--output table > "$EVIDENCE_DIR/164-312-e-1-tls-config.txt"
# §164.310(a)(1) — 网络访问控制
aws ec2 describe-vpc-endpoints \
--query 'VpcEndpoints[*].{Service:ServiceName,State:State,Type:VpcEndpointType}' \
--output table > "$EVIDENCE_DIR/164-310-a-1-vpc-endpoints.txt"
echo "证据收集完成。文件已保存至:$EVIDENCE_DIR/"
ls "$EVIDENCE_DIR/"
最佳实践总结
建议执行:在编写任何与HIPAA相关的基础设施代码之前,务必签署AWS业务合作伙伴协议。没有这份法律协议,所有的技术控制措施都将无效。
建议执行:使用由客户自行管理的、支持自动密钥轮换的KMS密钥。虽然也可以使用AWS管理的密钥,但这些密钥无法提供企业医疗审计人员所要求的关于密钥管理情况的证明文件。
建议执行:对于那些敏感度最高的电子健康信息字段(如社会安全号码、诊断结果、治疗记录等),必须实施字段级加密。仅进行存储加密是不足以防止拥有直接数据库访问权限的授权用户窃取数据的。
建议执行:对于用于存储审计日志的S3对象,应将其设置为“合规性”模式。处于“治理性”模式的S3对象可以被具有特殊权限的用户删除,而处于“合规性”模式的对象则任何人(包括root账户)都无法删除。
建议执行:每月都要运行一次预审计证据收集工具,而不仅仅是在审计之前进行这次操作。通过持续收集证据,可以确保你在任何时候都具备通过审计的条件。建议执行:在所有与AWS服务的通信中,都必须使用VPC端点。即使数据的来源地和目的地都在AWS内部,电子健康信息也不得通过公共互联网传输。
禁止执行:不要将电子健康信息的值记录到CloudWatch或应用程序日志中。应该记录的是某个字段被访问了这一事实,而不是该字段中存储了什么内容。
禁止执行:不要让多名工程师或自动化系统共享相同的IAM凭证。任何访问电子健康信息的实体都必须拥有唯一且可被审计的身份信息。禁止执行:不能认为仅仅因为某个工作负载位于VPC内部,就意味着它已经得到了隔离保护。实际上,安全组才是真正的防护边界;如果配置错误的安全组允许0.0.0.0/0地址在5432端口上进行访问,那么无论该RDS实例位于哪个VPC中,都会面临安全风险。
资源链接
相关文章
AWS为研讨会提供了免费的沙箱环境
AWS Builder Center现在为各类研讨会提供了免费且限时使用的沙箱环境,因此开发者不再需要使用自己的AWS账户和信用卡,也不必担心会出现意外费用。这一功能一直是社区用户长期以来的诉求之一,它有效解决了那些正在学习新AWS技术的从业者们所面临的最大障碍之一。 作者:Renato Losio
阅读全文
AWS推出了Amazon GuardDuty调查代理工具,用于自动化威胁分类与处理流程。
AWS发布了GuardDuty调查工具的公开预览版。该工具能够将调查结果、90天内的活动记录以及资源拓扑结构整合成结构化报告,这些报告中会包含风险等级、置信度评分以及MITRE ATT&CK分类信息。用户可以通过AWS MCP服务器使用这一工具进行调查,因此调查工作可以直接通过代理工具来执行。目前,每个账户每天最多只能使用10次这个预览版工具来进行调查。 作者:Steef-Jan Wiggers
阅读全文
人工智能评估工程:从零开始构建一款可用于生产环境的大型语言模型评估平台【完整使用手册】
一个令人印象深刻的演示与一个值得信赖的系统之间的差距,其实是通过各种评估来衡量的。 我想先讲一个目前正在数百个工程团队中发生的真实案例。 有一个团队为法律研究开发了一个RAG应用程序。他们用40个精心挑选的问题对该程序进行了测试,结果看起来很不错,于是便向合作方展示了这个系统。合作方对它印象深刻,随后便决定将其正式投入使用。 然而在系统投入生产三周后,一名法律助理发现其中一个答案错误地引用了某项法规。工程团队查看了相关数据,发现“准确性得分”为0.91,这个数值看起来是正常的;他们还检查了答案的相关性,结果也符合标准。 但他们忽略了一个重要的指标:即“上下文完整性”。这个指标用于判断系统是否检
阅读全文
如何让你的副业项目被人们注意到,并吸引到愿意付费使用的用户
2022年,我在业余时间开发了一个小型微服务产品,最终以几千美元的价格将其卖了出去。如今,有了人工智能工具的帮助,开发这样的产品可能会更加容易。 但真正发生巨大变化的是获取关注的成本,而不是开发软件本身的成本。 我认为,在2026年,产品的分发渠道将比开发本身更为重要。在这篇文章中,我会与大家分享我在产品开发过程中所学到的经验,并试图劝阻大家在开始下一个项目之前,先不要急着直接投入编码工作。 读完这份指南后,你应该能够掌握一些实用的方法和思路,这些方法可以帮助你将自己那些充满热情的项目推向市场。 需要明确的是,这篇文章主要是针对那些正在开发数字产品的人,尤其是软件产品。不过,这些概念同样适用于
阅读全文