我当前的Android应用程序要求用户使用用户名和密码登录。
Android应用程序调用REST Web服务进行用户登录,我不想以明文形式传输密码。
如何保护用户密码,以便服务器端可以识别/验证每个用户?
我目前正在尝试使用Jasypt库,如下所示:-
ConfigurablePasswordEncryptor passwordEncryptor = new ConfigurablePasswordEncryptor();
passwordEncryptor.setAlgorithm("SHA-1");
passwordEncryptor.setPlainDigest(true);
String encryptedPassword = passwordEncryptor.encryptPassword(userPassword);
...
if (passwordEncryptor.checkPassword(inputPassword, encryptedPassword)) {
// correct!
} else {
// bad login!
}
但是我的服务器端是用.NET编写的,据我了解Jasypt文档,密码加密器使用随机盐。
如何让我的服务器端代码与我发送的哈希用户密码匹配?
我所有的Web服务都具有HTTPS端点,这是否保证在交换访问令牌时没有人能“看到”我的用户密码“正在运行”?
答案 0 :(得分:0)
如果您使用Https(TLS),那么任何拦截网络的人都无法访问您的密码。
您应该在服务器端代码中而不是在客户端中哈希密码字符串
答案 1 :(得分:0)
您必须注意自己的工作。考虑实现常见的两要素密钥共享算法,例如 TOTP 。
客户端哈希是一种非常不常见但非常好的做法。当然,这不会阻止黑客登录用户的帐户,但会阻止他们获取可能重复使用的纯文本密码。
我建议在重置密码公式下完成电子邮件和密码的更改,这样就需要电子邮件/ SMS确认。
最后,在您这样做时,非常重要的是进行登录的连接是安全的,例如https / tls。
答案 2 :(得分:0)
一个好的解决方案是避免使用传统的“电子邮件/密码”方式进行身份验证,并采用此处对OTP或“一次性密码”的另一种建议。
考虑用户体验:在移动设备上键入电子邮件和密码既麻烦又烦人,笨拙。然后他们还必须记住密码吗?西方世界的普通人每天可能会使用10到15个应用程序,而我们想对他们的人员存储库征税,以获取另一个密码,以便他们在乘地铁的时候笨拙地键入他们的电话?
尽管将其组合起来看似具有挑战性,但请考虑使用一次密码。用户使用它输入电话号码作为识别令牌。
理论上,每个用户都有自己的唯一电话号码,这很容易使用户记住。由于您的用户使用的是Android设备,因此到目前为止,对吗?而且,不用麻烦地输入电子邮件和密码。
在他们输入他们的电话号码后,我们然后向他们发送代码到移动设备,这是一个4到6位的数字。用户在应用程序中输入该代码,从而证明他们是该电话号码所绑定的设备的所有者。
与电子邮件/密码相比,OTP的好处在于,用户部分所需的存储空间很小。是的,它甚至比OAuth还要好,因为如果用户从未通过移动浏览器登录Gmail帐户或Github帐户怎么办?然后,他们返回到移动设备的电子邮件/密码尴尬样式身份验证。
一次性密码是用户友好的。
但是您说的还可以,但是它是否安全,并且对这个问题更重要... 我如何让我的服务器端代码与我发送的哈希用户密码相匹配?
对,因此一次性密码技术始终是进行IMO的一项雄心勃勃的项目。
因此,我们需要保留用户应输入到设备中的代码,以便我们将来可以在某些时候进行比较。生成代码时,请将其保存到Firebase,以便将来将来可以联系Firebase,并说电话号码212-555-1212的用户刚给您发送了代码1234,这是正确的代码吗? / p>
因此,Firebase与OTP配合使用的方式是可以将代码存储在Firebase中。然而,挑战实际上是向用户发送代码。这是一条实际的SMS消息。为了解决这个问题,您不能单独使用Firebase,而可以集成非常流行的Twilio。 Twilio就是通过电话SMS消息与用户进行交互,因此我们可以利用Twilio向用户发送代码。
您还可以在Firebase中处理身份验证或用户系统。用户输入OTP后,我们将通过Firebase生成JSON Web令牌。
因此,所有JSON存储和反映用户身份的所有信息都可以保存在Firebase上。
但是我没有回答的问题还有另一部分:
我该如何保护用户密码,以便服务器端 可以识别/验证每个用户?
好的,因此您需要在某些服务器上比较代码。它不能是Firebase,因为Firebase只是一个数据存储,它是一个存储JSON数据的地方,它不能让我们运行自定义代码。
那么,您是否编写用于代码比较的服务器?我们不想在用户的设备上进行此比较。
那我们该怎么办?另外,我们如何生成代码?也不要为此使用用户的设备。
那么我们在哪里生成代码?我们知道可以使用Firebase数据存储来存储代码,但是如何生成代码?
对于Google Cloud Functions来说,这是一项很好的工作。
因此,Google Cloud Functions是可在Google服务器上按需运行一次的代码段。 GCF具有紧密的互操作性,并与Firebase数据存储集成。
我们可以为Firebase内部的数据添加一些逻辑或处理。 GCF将允许您使用一些自定义逻辑来生成代码并将其保存到Firebase,一旦用户发送代码,GCF还可以比较代码。
AWS Lambda和GCF在功能上几乎相同,因此也可以选择。
答案 3 :(得分:-1)
在客户端(移动应用程序)和服务器之间实现身份验证和授权时,需要考虑几件事。
首先,您的服务器必须使用哪种身份验证和授权机制来请求api端点? (是否是双向验证?基于承载令牌(授予类型的用户名和密码)吗?是否基于承载令牌(grant-type access-token
)?
第二,正如您提到的,服务器编程是基于.Net的,但是您能否更具体地说明您的服务层(Api)是用WebApi 2还是OData编写的?
最后,您的服务器是否允许使用SSH或不使用SSH(即HTTP vs HTTPS)进行通信?如果使用SSH,则可以通过其他方式传输用户凭据(即用户名和密码),因此永远不会通过HTTP传输凭据。
然后只有它在您的终端出现,即在Android Mobile App中根据服务器与api端点进行通信的要求来实施身份验证和授权机制。
例如,我的服务器需要实施基于令牌的身份验证(承载者令牌和grant-type password
)以发出每个服务器请求(GET, POST, DELETE, PUT)
,而我已经使用改造客户端来实现了,如:
public Retrofit getRetrofitClient() {
// first add the authorization header
OkHttpClient mOkClient = new OkHttpClient.Builder().addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request newRequest = chain.request().newBuilder()
.addHeader("Authorization", "XXXXXXXXXXXX")
.build();
return chain.proceed(newRequest);
}
}).build();
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.client(mOkClient)
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
.build();
}
return retrofit;
}
我的服务是
public interface LoginService {
@POST("/api/token")
@FormUrlEncoded
Call<TokenModel> getToken(@Field("username") String username,
@Field("password") String password,
@Field("grant_type") String grantType);
}
现在,我可以在每个请求中使用此令牌与服务器通信。我不需要在公共互联网上传输用户名和密码,而是仅使用令牌,并且它具有24小时的过期时间(因为服务器已实现了此令牌的过期日期)。
希望它可以帮助您了解cleint(Android Mobile App)与服务器之间的身份验证和授权机制。