在任何的开发过程中,只要设计到安全的问题,我们都需要牢记Shannon的这句话,虽然是与密码学相关,但是也可以应用到服务的安全,也就是说,我们应该假定攻击者最终将完全熟悉这个系统。
作为交付业务的开发人员来说,安全从来都是一个重要的话题,并且如果是从事健康、金融等相关领域,在北美的合规性上更是尤甚。除了从业务上对安全做的一些考虑,比如密码强度,Multi-Factor Authentication(MFA),更多的安全相关性可能对于一个正经开发人员来说,可能很难面面俱到的考虑周全。在此,想借用一个和健康医疗相关的项目来对项目上所面临的安全需求以及实践进行介绍。
背景
除了在业务上我们满足用户的安全需求,以及一些对常规的离散的安全了解(TLS,injection,DoS等),我们没有一个全局的系统的安全方面的考虑,再加上客户也提出了一条条没有组织没有结构的安全需求,在与客户对话之前需要做大量的讨论和研究。
对于计算机安全的定义来说,机密性、隐私性和完整性是三个关键目标,如果从计算机安全模型来说,又包括硬件、软件和通信等。对于这样的分类和定义,如果我们能很好的做出威胁建模,那么结果可能是比较全面,但是问题又来了,在组内大多数成员没有威胁建模的经验下,我们又很难做好一个威胁建模,这样我们的结论也可能达不到我们想要的目标。
那么这样的话,从一个比较直观并且概括的层面,能让大家了解我们可能面对的保护和攻击,可以在最基本的面上有个大概了解。
应用 Application
认证授权
- 所有需要认证的请求会通过Gateway Ambassador,通过 Ambassador 提供的 Filter 和 FilterPolicy 来控制到认证的接口上
- 不同接口会有不同的权限,这一层是在应用层上实现
- 所有的用户只能访问属于自己的 tenant 内的资源,这也是在应用层上实现
漏洞
对于这类攻击其实我们经常能听到很多,比如 SQL injection, XSS 等等。这些攻击在我们目前使用的通用框架中其实已经帮助我们做了很好的保护,比如现在的 ORM 框架早已有 Parameterized queries 来防止 injection(前提是我们不要去拼接 query),Spring Security 也提供了 CSP header 来保护 XSS。
再比如CSRF,在目前我了解到的使用 Spring 的应用中,都是disable的状态。因为其实如果我们是使用的JWT token来做认证,而不是基于 cookie 来做认证,那么我们也不用做更多来防止 CSRF。
日志
在我们的实施方案中,我们对日志进行了不同的分类。一类是基本的服务应用日志,主要便于生产环境的问题识别;另一类是审计日志,主要是记录用户的行为,包括哪个用户从某个 IP 做了什么样的请求操作,也可以防止用户抵赖(Repudiation of Action)。
GCP 中的日志服务提供了 Log buckets,我们对以上两类日志分别放到了不同的 bucket 里面,这样也可以对于不同的日志设置不同的 retention period。
邮件
邮件的安全可能是我们容易忽略的一个问题,在邮件上设计到的安全有DMARC, SPF 和 DKIM。因为项目上使用的是邮件服务Sendgrid,所以对于 DMARC, SPF 和 DKIM 是在邮件服务中实施的。在这里是想让大家可以了解到即便是邮件功能,也不能忽略其安全的地位。
基础设施 Infrastructure
网络
如果实在信赖的 VPC 之内,我们可以将 TLS 在 Load Balancer 就终止,任何在这个 VPC 内的流量都是以解密之后传输的。但其实在 cluster 内服务和服务之间的安全也是需要保证的。
Firewall 的正确配置,开启 DNSSEC,配置 Egress 到信任的外部服务,利用 WAF 来控制服务的访问等等,这些都是在网络上我们可以考虑的安全要素,因为网络安全是一个比较大的另一个话题,并且我知识也有限,就不展开讲更多。
Security Posture Monitoring
我们需要知道我们服务的资产,并且哪些资产在业务上有重要意义,我们还需要知道我们做了哪些安全措施来保护我们的资产。
部署在 GCP 之上的资产,GCP 的 Security Command Center 可以帮助我们了解和修补 GCP 的安全和风险。其提供了不同等级的服务,详细的可以参考 Security Command Center。
密码秘钥轮训
定期或者主动去轮换现有的密码。
GCP 的 Secret Manager 配合 pubsub 和 CloudFunction 可以设置 rotation period 来帮助我们定期更改密码,但是我们的密码有些是集成了第三方系统的 api key 或者是 private key,这样不太方便使用 Secret Manager 提供的 rotation 功能。对于这样的第三方密码,还是需要运维人员手动在第三方服务中更新密码,或者使用其提供的 API 或者 Script 来重新生成密码,然后用 Terraform 控制 GCP Secret Manager 来帮助我们管理密码。
但这里有个问题是密码是不能明文存储在对应的 Terraform repo 中,所以目前我们在项目中只是将密码文件加密后再上传,对于 Terraform 来更新密码还是在本地执行 terraform apply,还没有一个比较有效的方式。
安全检查 / 测试 Security Check / Testing
静态扫描
我们可以使用很多静态扫描工具帮助我们提高代码质量,也可以帮助我们在代码层面上泄露安全风险。
这些工具会集成到我们的 CI 之上,比如 gitleaks 来帮助我们检查是否有硬编码的密码、私钥等信息,OWASP Dependency Check 来检查 vulnerability。
动态扫描
除了静态扫描外,动态扫描可以帮助我们检查出应用服务上的安全风险,比如之前提到的 XSS,injection 等等,都可以利用周期性的动态扫描来规避风险。
虽然不能完全依赖这类的扫描工具来保护我们的应用服务,但是这在一定程度上可以缓解风险的可能性。
Mobile
Run Application Self Protection RASP
对于大多数应用的外围保护来说,比如防火墙,IDS,这些保护都只是对运行环境的保护,但是设计到应用本身,这样的保护不会具有针对性,也就是说突破了这些保护的限制,一样能对应用造成威胁。
那么移动端App,不像服务端的应用部署在一个几乎完全受我们控制的环境中,它可能运行在一个已经过时很久,或者不太安全的版本的 OS 上。这时候需要一个能提供自我保护的应用。
在绿码项目中,我们使用的是客户合作的Promon SHIELD,其提供了比如 root detection, code obfuscation, code injection protection 和 screen reader protection 等等可配置的保护。
RASP 更多的是对应用本身提供保护,所以是其实现方式我理解无非是在应用内,比如针对应用特定场景的保护,或者是在应用外有一层保护膜,比如使用 Promon SHIELD 就是一层 wrapper,如下:
App Attestation
就如最开始提到的,我们在设计系统时就要考虑到攻击者最终会完全熟悉我们的系统。那么设想攻击者有了这些知识后,能不能做出一个和我们完全一模一样的App。
如果在这个时候我们怎样去防止这种类似钓鱼的攻击发生。对于 iOS 和 Android 都有方式去做 attestation。
在我们项目上,对于Android 采用的是 SafetyNet Attestation,而iOS是利用通知推送的机制的形式。虽然本身 Android 也有通知推送机制,但是其推送地址和这个合法的 App 之间的关系仅仅是 package name,然而iOS的推送机制是基于 push certificate,所以对于 Android 使用的是其提供的 SafetyNet 更为可靠。
以下是简单的流程图,感兴趣的同事可以进一步深究。
结语
在这里只是简单的从一个普通正经开发者的安全角度出发,列举了一些其他项目可能可以参考的安全实践。但是安全远远不止于此,并且涉及到的知识也是非常之广,以上提到的任何一点都可以有更深的讨论。