15

I am using Dart mailer in Flutter and there is a comment that says:

How you use and store passwords is up to you. Beware of storing passwords in plain.

Is there any way to hash the password? How can I avoid storing it in plain text?

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
Zeusox
  • 7,708
  • 10
  • 31
  • 60
  • If the service the credentials are for does not provide authentication tokens (Google Auth does) then the best option is to write a plugin for the AccountManager as mentioned in the accepted answer of https://stackoverflow.com/questions/1925486/android-storing-username-and-password. See also the comments below the answer. – Günter Zöchbauer Aug 02 '18 at 16:31
  • 1
    I am not using this for authentication. I am trying to send emails using Dart Mailer Package and they note not to store passwords in plain ? Please check this link out to kinda see what I am talking about. Thanks, https://pub.dartlang.org/packages/mailer – Zeusox Aug 02 '18 at 16:53
  • That doesn't seem to support the token option :-/ – Günter Zöchbauer Aug 02 '18 at 16:59
  • So in this case, just store password in plain ? – Zeusox Aug 02 '18 at 17:06
  • The best solution would probably be a plugin for the Accountmanager, but that's probably quite a bit of work. – Günter Zöchbauer Aug 02 '18 at 17:13
  • Accountmanager for flutter ? – Zeusox Aug 02 '18 at 17:14
  • AccountManger for Android. Plugins are for making native features available to Dart. I don't know if iOS provides something similar. – Günter Zöchbauer Aug 02 '18 at 17:17
  • What is the worst case scenario of using gmail account password in plain ? Given that this is used just to send automatic emails ! – Zeusox Aug 02 '18 at 17:21
  • It's probably the users Gamil account password and I wouldn't expect people being happy for that being exposed. – Günter Zöchbauer Aug 02 '18 at 17:29
  • Well, I think we are talking about different things. Let me put this in a context, This app takes in users feedback and stores it on a Firestore Database. When the store process is complete, I automatically send an email to our service email with all the data that was stored on the Database. So in a nutshell, it does not authenticate users, it uses our company email as credentials so the apps sends emails automatically to another email. – Zeusox Aug 02 '18 at 17:35
  • And what is the password for? Whose password is it, for what account/service? – Günter Zöchbauer Aug 02 '18 at 17:38
  • The password is GmailSmtpOptions as in " var options = new GmailSmtpOptions()" and " ..username = 'your gmail username' | ..password = 'your gmail password';" so I can use GmailSmtp to send emails... – Zeusox Aug 02 '18 at 17:41

3 Answers3

16

It is generally not a good idea to store passwords in plain text anywhere. The way you handle passwords, though, depends on the platform.

Flutter

The flutter_secure_storage package uses Keychain on iOS and KeyStore on Android to store passwords (or tokens).

// Create storage
final storage = FlutterSecureStorage();

// Read secret 
String value = await storage.read(key: key);

// Write secret 
await storage.write(key: key, value: value);

Note that for Android the min API is 18.

Dart Server

If you are making a server, it is even more important not to store the user passwords in plain text. If the server is compromised, the attacker would have access to all of the passwords, and many users use the same password on multiple accounts.

It would be best to hand the authentication over to Google or Facebook or some other trusted third party by using OAuth2. However, if you are doing your own authorization, you should hash the passwords with a salt and save the hash, not the password itself. This makes it more difficult for an attacker to get the user passwords in case the server is compromised.

A basic implementation (but see comment below) could use the crypto package by the Dart Team.

// import 'package:crypto/crypto.dart';
// import 'dart:convert';

var password = 'password123';
var salt = 'UVocjgjgXg8P7zIsC93kKlRU8sPbTBhsAMFLnLUPDRYFIWAk';
var saltedPassword = salt + password;
var bytes = utf8.encode(saltedPassword);
var hash = sha256.convert(bytes);

Save the salt and the hash. Discard the password. Use a different salt for every user.

To make brute forcing the hashes more difficult, you can also check out the dbcrypt package.

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
  • 1
    You should skip the “basic implementation” part here entirely. A single round (of SHA-256 or otherwise) has been considered mishandling of user passwords since the 70s. – Ry- Jul 16 '22 at 00:47
  • @Ry-, Thanks. I'm not a security expert so I appreciate when people like you point these things out. I didn't delete the "basic implementation" because I like understanding how it works, but I linked to your comment. – Suragch Jul 16 '22 at 17:49
  • @Ry- Can you suggest or show an example of the good practice to hash to password on the dart server side? – Sittiphan Sittisak Nov 19 '22 at 04:54
  • 1
    @SittiphanSittisak: bcrypt as seen but mysteriously deemphasized in the answer is one better option. – Ry- Nov 19 '22 at 20:52
1

If you want to hash

Use the password_hash package. Their example code is very easy to use:

var generator = new PBKDF2();
var salt = Salt.generateAsBase64String();
var hash = generator.generateKey("mytopsecretpassword", salt, 1000, 32);

Store both the hash and the salt, and you can verify someone else's password attempt by running the generator.generateKey function using their password and the saved salt.

What you actually want

If you're trying to automatically login, you of course need the original password, not a hash. You have a couple options

  1. If the device that will have your app installed is safe, as in it is some company-owned device that has to be logged into by an employee, then have it in plaintext. It doesn't matter. As any company's security policy should be, you must make sure that hard drives are wiped before disposing of electronics (And make sure that no one can stroll in and take the iPad or whatever it is).

  2. If unknown people outside of your organization will be installing your app, you will have to have them login and use their email, or have an API open that will send emails on their behalf (To prevent spamming from your email). The app would sent a POST to that API to send an email. If you had the plaintext password in the application, they could find it on their device, and abuse it.

Nicholas Pipitone
  • 4,002
  • 4
  • 24
  • 39
  • It does not work? When I use the hash package, the Gmail smtp says I have wrong credentials ? – Zeusox Aug 02 '18 at 16:15
  • I don't understand, what are you doing? – Nicholas Pipitone Aug 02 '18 at 17:11
  • I am using Dar mailer as in https://pub.dartlang.org/packages/mailer . and they note to store the password in plain ? How can I avoid that ? – Zeusox Aug 02 '18 at 17:13
  • What exactly are you doing though? On a high level, what are you trying to accomplish with your app? – Nicholas Pipitone Aug 02 '18 at 17:15
  • This app takes in users feedback and stores it on a Firestore Database. When the store process is complete, I automatically send an email to our service email with all the data that was stored on the Database. – Zeusox Aug 02 '18 at 17:16
  • Ah, and you want it to automatically login via SMTP and send the email. Is this running on a user's application (ie on their device, not your server)? – Nicholas Pipitone Aug 02 '18 at 17:32
  • that's a very bad idea. is there no way to send emails directly from firebase, or let firebase trigger some email service? – boformer Aug 02 '18 at 22:08
0

This response comes late, but here is my approach to storing and using a password for sending emails to recipients using mailer in Flutter. I hope it helps anyone facing this issue.

First I downloaded the crypton package. Then I created a separate dart file where I handle everything related to sending mails, I called it mailer. In this file is where I specify the password, encrypts it using crypton, and use send the email using the decrypted password.

Below is the code of my mailer.dart file:

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:mailer/mailer.dart';
import 'package:mailer/smtp_server.dart';
import 'package:intl/intl.dart';
import 'package:crypton/crypton.dart';

class Mailer {
  //the variable below we use to encrypt and decrypt the password
  RSAKeypair _rsaKeypair = RSAKeypair.fromRandom();
  //Below we set the password as a private variable
  String _password = 'mySecurePassword';
  
  //We set an encrypted variable that will store the encrypted password;
  String _encrypted;

  //The function below sets the encrypted variable by assigning it the encrypted value of the password
  void setEncrypt () {
    _encrypted = _rsaKeypair.publicKey.encrypt(_password);
  }

  //The function below is responsible for sending the email to the recipients and it is what we call when we want to send an email   
  emailSender({@required String emailRecipient, @required List<String> paths}) async {
    //We call the setEncrypt() function to assign the correct value to the encrypted variable
    setEncrypt();
    String username = 'email@email.com';
    //We asign the decrypted value of encrypted to the password we provide to the smtpServer 
    String password = _rsaKeypair.privateKey.decrypt(_encrypted);

    //The rest of sending an email is the same.

    final smtpServer = gmail(username, password);
    // Use the SmtpServer class to configure an SMTP server:
    // final smtpServer = SmtpServer('smtp.domain.com');
    // See the named arguments of SmtpServer for further configuration
    // options.

    // Create our message.
    Message message = Message()
      ..from = Address(username, 'Your name')
      ..recipients.add(emailRecipient)
      ..ccRecipients.addAll(['secondEmail@email.com'])
      ..subject = 'Date: ${DateFormat('dd/MM/yyyy').format(DateTime.now())}'
      ..text = 'This is the plain text.\nThis is line 2 of the text part.'
      ..html = "<h1>Hi:</h1>\n<p>This is some html</p>\n<p>Greetings, mailer.dart</p>";

    for (String path in paths) {
      message
        ..attachments.add(
          FileAttachment(
            File(
              path,
            ),
          ),
        );
    }
    var connection = PersistentConnection(smtpServer);

    // Send the first message
    await connection.send(message);

    // send the equivalent message
    //await connection.send(equivalentMessage);

    // close the connection
    await connection.close();
  }
}

This was my approach to solving the issue of storing passwords as plain text for sending emails using the mailer package or any package with a similar purpose.

Peter Csala
  • 17,736
  • 16
  • 35
  • 75
Diego Molina
  • 35
  • 1
  • 8