如何在Android客户端和服务器端应用程序之间安全地发送密码?

时间:2019-01-01 09:59:08

标签: android .net password-hash jasypt

我当前的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端点,这是否保证在交换访问令牌时没有人能“看到”我的用户密码“正在运行”?

4 个答案:

答案 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)与服务器之间的身份验证和授权机制。