前两天面试一个remote职位,一个技术型的面试官,问的特别细致。
还记得其中一个是关于Github action的安全措施的。
这个考官也十分新奇,问的都很抽象。
他就问我在Github action中,都采用了什么样的安全措施。
我当时也是有点蒙,细节部分当时确实不是很清楚,直接说了句把重要信息都存在了Parameter Store来敷衍了过去。
所以也是为了给自己补补课,打算重新再认真总结下WEB系统常见的安全措施。
WEB安全,主要分为两个大块。一个是Application层面,一个是Network层面。Network的话,我自己也还不是特别熟悉,这次先主要总结下Application层面常见的应对措施。
- 账号安全
- CORS
- Cross Site Scripting
- SQL Injection
- ClickJacking
- POST、GET等Request Method的正确使用
- 系统信息的加密,hash化
账号安全
主要包含账号注册,login,logout相关部分的处理。
账号注册方面:
・要注意不能使用太简单的密码,密码要经过加密处理
・为了避免恶意注册,要添加2段认证,比如通过email注册
login:
・页面里的auto complete最好关闭
・如果使用java的session管理功能的话,login前后的JSESSIONID务必要刷新
・为了防止暴力破解,要加入login尝试次数限制的机制
logout:
・logout不只是页面的迁移,之前的认证key也需要进行invalidate处理
CORS
使用API时有时会需要跨域,最好通过ALB的设置解决,商业环境ajax的跨域设置最好要关闭。Spring的话可以设置某些指定环境才打开CORS。
@Configuration
@Profile("default", "develop", "staging")
class CorsConfig {
@Bean
fun corsFilter(): FilterRegistrationBean<*> {
val source = UrlBasedCorsConfigurationSource()
val config = CorsConfiguration()
config.allowCredentials = true
config.addAllowedOrigin(CorsConfiguration.ALL)
config.addAllowedHeader(CorsConfiguration.ALL)
config.addAllowedMethod(CorsConfiguration.ALL)
source.registerCorsConfiguration("/**", config)
val bean = FilterRegistrationBean(CorsFilter(source))
bean.setOrder(Ordered.HIGHEST_PRECEDENCE)
return bean
}
}
Cross Site Scripting
通过往数据库里写入script,然后在画面显示的时候去执行这些script。以用来窃取用户本地的信息。
SQL Injection
一般的系统安全的书中都会讲到,大概是10几年前比较常见的攻击方式吧。就是通过在query中注入一些查询语句,然后把查询内容显示到客户端。
CrossSiteScripting跟SQL Injection,早期web框架不成熟的时候应对起来比较麻烦,现在很多web框架都提供了相应的功能,相对来说对应也变得简单了。像在Spring中,就可以通过BeanValidation去限制< ‘等特殊字符的提交来解决问题。
下面这段示例代码就是对特殊字符做了限制,然后在DTO中通过使用 @field: IllegalWords 就可以(当前还需要@Valid使BeanValidation有效)了。
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@ReportAsSingleViolation
@Constraint(validatedBy = [IllegalWordsValidator::class])
annotation class IllegalWords(
val message: String = "validate.illegalwords",
val groups: Array<KClass<out Any>> = [],
val payload: Array<KClass<out Payload>> = []
)
class IllegalWordsValidator: ConstraintValidator<IllegalWords, String> {
override fun initialize(constraintAnnotation: IllegalWords) {}
override fun isValid(value: String?, context: ConstraintValidatorContext?): Boolean {
return if (value == null || value.isEmpty()) {
true
} else {
return !IllegalWordsValidator.PATTERN_ILLEGAL.matcher(value).find()
}
}
companion object {
private val PATTERN_ILLEGAL = Pattern.compile("[&<>\'\"]")
}
}
ClickJacking
POST、GET等Request Method的正确使用
这部分在脆弱性测试里面经常出现。尤其是可以用来定位用户的信息,最好不要通过GET的参数去请求(比如userId、名字等个人信息等)。
系统信息的加密,hash化
这部分主要是内部管理相关。如果你的source code被某个开发小哥copy出去了,然后source里面包含着访问数据库或者服务器的key和serect,那么事情是不是就很糟糕了。所以为了避免这种情况,访问各种服务器的密码最好不要以明文的形式写在代码里面。以我当前碰到的情况的话,解决方案的大概有三种:
・以环境变量的形式写入到服务器中(比如使用提前在服务器中设置好)
・将密码存储到ParameterStore中
・将密码写在github repository的setting里
# github secrets使用示例
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-1