Merge pull request #2017 from kounoike/pr-secure-password

use PBKDF2 for password
This commit is contained in:
Naoki Takezoe
2018-05-24 10:16:31 +09:00
committed by GitHub
6 changed files with 48 additions and 5 deletions

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<changeSet>
<modifyDataType columnName="PASSWORD" newDataType="varchar(200)" tableName="ACCOUNT"/>
</changeSet>

View File

@@ -53,5 +53,6 @@ object GitBucketCoreModule
new Version("4.23.0", new LiquibaseMigration("update/gitbucket-core_4.23.xml")), new Version("4.23.0", new LiquibaseMigration("update/gitbucket-core_4.23.xml")),
new Version("4.23.1"), new Version("4.23.1"),
new Version("4.24.0", new LiquibaseMigration("update/gitbucket-core_4.24.xml")), new Version("4.24.0", new LiquibaseMigration("update/gitbucket-core_4.24.xml")),
new Version("4.24.1") new Version("4.24.1"),
new Version("4.25.0", new LiquibaseMigration("update/gitbucket-core_4.25.xml"))
) )

View File

@@ -322,7 +322,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
account => account =>
updateAccount( updateAccount(
account.copy( account.copy(
password = form.password.map(sha1).getOrElse(account.password), password = form.password.map(pbkdf2_sha256).getOrElse(account.password),
fullName = form.fullName, fullName = form.fullName,
mailAddress = form.mailAddress, mailAddress = form.mailAddress,
description = form.description, description = form.description,
@@ -563,7 +563,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
if (context.settings.allowAccountRegistration) { if (context.settings.allowAccountRegistration) {
createAccount( createAccount(
form.userName, form.userName,
sha1(form.password), pbkdf2_sha256(form.password),
form.fullName, form.fullName,
form.mailAddress, form.mailAddress,
false, false,

View File

@@ -417,7 +417,7 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
post("/admin/users/_newuser", newUserForm)(adminOnly { form => post("/admin/users/_newuser", newUserForm)(adminOnly { form =>
createAccount( createAccount(
form.userName, form.userName,
sha1(form.password), pbkdf2_sha256(form.password),
form.fullName, form.fullName,
form.mailAddress, form.mailAddress,
form.isAdmin, form.isAdmin,
@@ -457,7 +457,7 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
updateAccount( updateAccount(
account.copy( account.copy(
password = form.password.map(sha1).getOrElse(account.password), password = form.password.map(pbkdf2_sha256).getOrElse(account.password),
fullName = form.fullName, fullName = form.fullName,
mailAddress = form.mailAddress, mailAddress = form.mailAddress,
isAdmin = form.isAdmin, isAdmin = form.isAdmin,

View File

@@ -33,7 +33,16 @@ trait AccountService {
* Authenticate by internal database. * Authenticate by internal database.
*/ */
private def defaultAuthentication(userName: String, password: String)(implicit s: Session) = { private def defaultAuthentication(userName: String, password: String)(implicit s: Session) = {
val pbkdf2re = """^\$pbkdf2-sha256\$(\d+)\$([0-9a-zA-Z+/=]+)\$([0-9a-zA-Z+/=]+)$""".r
getAccountByUserName(userName).collect { getAccountByUserName(userName).collect {
case account if !account.isGroupAccount =>
account.password match {
case pbkdf2re(iter, salt, hash) if (pbkdf2_sha256(iter.toInt, salt, password) == hash) => Some(account)
case p if p == sha1(password) =>
updateAccount(account.copy(password = pbkdf2_sha256(password)))
Some(account)
case _ => None
}
case account if (!account.isGroupAccount && account.password == sha1(password)) => Some(account) case account if (!account.isGroupAccount && account.password == sha1(password)) => Some(account)
} getOrElse None } getOrElse None
} }

View File

@@ -1,10 +1,13 @@
package gitbucket.core.util package gitbucket.core.util
import java.net.{URLDecoder, URLEncoder} import java.net.{URLDecoder, URLEncoder}
import java.security.SecureRandom
import java.util.{Base64, UUID} import java.util.{Base64, UUID}
import org.mozilla.universalchardet.UniversalDetector import org.mozilla.universalchardet.UniversalDetector
import SyntaxSugars._ import SyntaxSugars._
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.PBEKeySpec
import org.apache.commons.io.input.BOMInputStream import org.apache.commons.io.input.BOMInputStream
import org.apache.commons.io.IOUtils import org.apache.commons.io.IOUtils
@@ -16,6 +19,32 @@ object StringUtil {
UUID.randomUUID().toString.substring(0, 16) UUID.randomUUID().toString.substring(0, 16)
} }
def base64Encode(value: Array[Byte]): String = {
Base64.getEncoder.encodeToString(value)
}
def base64Decode(value: String): Array[Byte] = {
Base64.getDecoder.decode(value)
}
def pbkdf2_sha256(iter: Int, salt: String, value: String): String = {
val keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
val ks = new PBEKeySpec(value.toCharArray, base64Decode(salt), iter, 256)
val s = keyFactory.generateSecret(ks)
base64Encode(s.getEncoded)
}
def pbkdf2_sha256(value: String) = {
val keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
val secureRandom = new SecureRandom
val salt: Array[Byte] = new Array(32)
secureRandom.nextBytes(salt)
val iter = 100000
val ks = new PBEKeySpec(value.toCharArray, salt, iter, 256)
val s = keyFactory.generateSecret(ks)
s"""$$pbkdf2-sha256$$${iter}$$${base64Encode(salt)}$$${base64Encode(s.getEncoded)}"""
}
def sha1(value: String): String = def sha1(value: String): String =
defining(java.security.MessageDigest.getInstance("SHA-1")) { md => defining(java.security.MessageDigest.getInstance("SHA-1")) { md =>
md.update(value.getBytes) md.update(value.getBytes)