mirror of
https://github.com/kokonect-link/cherrypick
synced 2025-01-23 02:04:33 +09:00
parent
1a4cee0dc3
commit
ab6a5d0c3d
@ -33,7 +33,7 @@ Misskey의 전체 변경 사항을 확인하려면, [CHANGELOG.md#2024xx](CHANGE
|
||||
- 활성화하면 글자를 표시하기 위한 여유 공간이 좁을 때 디자인이 상대적으로 어색하게 보일 수 있으며, 실험실 기능이므로 이 기능이 변경하는 부분을 확실히 알고 있는 사용자만 활성화할 것을 권장합니다.
|
||||
- 비밀번호 해싱 알고리즘이 `bcrypt`에서 `argon2`로 변경됨
|
||||
- 이 변경으로 이후에 비밀번호를 변경하거나 신규로 가입한 사용자는 `argon2`를 사용하여 비밀번호 해시가 생성됩니다.
|
||||
- 이전에 가입한 사용자는 비밀번호를 변경하지 않아도 `bcypt`를 사용할 수 있으며 여전히 기존과 동일하게 호환됩니다.
|
||||
- 이전에 가입한 사용자는 로그인 시 자동으로 `bcypt`에서 `argon2`로 해시가 변경됩니다.
|
||||
|
||||
### General
|
||||
- Feat: 위젯 영역을 숨길 수 있음
|
||||
@ -89,7 +89,7 @@ Misskey의 전체 변경 사항을 확인하려면, [CHANGELOG.md#2024xx](CHANGE
|
||||
- Fix: 노트 본문의 사용자 멘션 영역을 클릭하면 노트 상세 페이지가 표시됨
|
||||
|
||||
### Server
|
||||
- Enhance: 보안 향상을 위해 비밀번호 해싱 알고리즘이 `bcrypt`에서 `argon2`로 변경됨 (kokonect-link/cherrypick#511)
|
||||
- Enhance: 보안 향상을 위해 비밀번호 해싱 알고리즘이 `bcrypt`에서 `argon2`로 변경됨 (kokonect-link/cherrypick#511), (1673beta/cherrypick#88)
|
||||
- 이제 72 바이트를 초과하는 비밀번호를 사용할 수 있습니다.
|
||||
- 이로써 `Sharkey`, `FireFish`, `IceShrimp` 등의 클라이언트에서 `CherryPick`으로 이전할 때 암호 호환성이 보장됩니다.
|
||||
- Fix: 이모지를 등록하거나 가져오려고 할 때 오류가 발생할 수 있음 (kokonect-link/cherrypick#487), (kokonect-link/cherrypick#508)
|
||||
|
@ -5,8 +5,6 @@
|
||||
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import * as argon2 from 'argon2';
|
||||
//import bcrypt from 'bcryptjs';
|
||||
import { IsNull, DataSource } from 'typeorm';
|
||||
import { genRsaKeyPair } from '@/misc/gen-key-pair.js';
|
||||
import { MiUser } from '@/models/User.js';
|
||||
@ -17,6 +15,7 @@ import { MiUsedUsername } from '@/models/UsedUsername.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import generateNativeUserToken from '@/misc/generate-native-user-token.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { hashPassword } from '@/misc/password.js';
|
||||
|
||||
@Injectable()
|
||||
export class CreateSystemUserService {
|
||||
@ -34,7 +33,7 @@ export class CreateSystemUserService {
|
||||
|
||||
// Generate hash of password
|
||||
//const salt = await bcrypt.genSalt(8);
|
||||
const hash = await argon2.hash(password);
|
||||
const hash = await hashPassword(password);
|
||||
|
||||
// Generate secret
|
||||
const secret = generateNativeUserToken();
|
||||
|
@ -5,8 +5,6 @@
|
||||
|
||||
import { generateKeyPair } from 'node:crypto';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
//import bcrypt from 'bcryptjs';
|
||||
import * as argon2 from 'argon2';
|
||||
import { DataSource, IsNull } from 'typeorm';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { MiMeta, UsedUsernamesRepository, UsersRepository } from '@/models/_.js';
|
||||
@ -22,6 +20,7 @@ import { bindThis } from '@/decorators.js';
|
||||
import UsersChart from '@/core/chart/charts/users.js';
|
||||
import { UtilityService } from '@/core/UtilityService.js';
|
||||
import { UserService } from '@/core/UserService.js';
|
||||
import { hashPassword } from '@/misc/password.js';
|
||||
|
||||
@Injectable()
|
||||
export class SignupService {
|
||||
@ -70,8 +69,7 @@ export class SignupService {
|
||||
}
|
||||
|
||||
// Generate hash of password
|
||||
//const salt = await bcrypt.genSalt(8);
|
||||
hash = await argon2.hash(password);
|
||||
hash = await hashPassword(password);
|
||||
}
|
||||
|
||||
// Generate secret
|
||||
|
20
packages/backend/src/misc/password.ts
Normal file
20
packages/backend/src/misc/password.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { randomBytes } from 'crypto';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import * as argon2 from 'argon2';
|
||||
|
||||
export async function hashPassword(password: string): Promise<string> {
|
||||
const salt = randomBytes(32);
|
||||
return argon2.hash(password, { salt: salt, type: argon2.argon2id });
|
||||
}
|
||||
|
||||
export async function comparePassword(password: string, hash: string): Promise<boolean> {
|
||||
if (isOldAlgorithm(hash)) {
|
||||
return bcrypt.compare(password, hash);
|
||||
}
|
||||
|
||||
return argon2.verify(hash, password);
|
||||
}
|
||||
|
||||
export function isOldAlgorithm(hash: string): boolean {
|
||||
return hash.startsWith('$2');
|
||||
}
|
@ -4,10 +4,9 @@
|
||||
*/
|
||||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import * as argon2 from 'argon2';
|
||||
import * as OTPAuth from 'otpauth';
|
||||
import { IsNull } from 'typeorm';
|
||||
import { comparePassword, hashPassword, isOldAlgorithm } from '@/misc/password.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type {
|
||||
SigninsRepository,
|
||||
@ -124,7 +123,12 @@ export class SigninApiService {
|
||||
const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id });
|
||||
|
||||
// Compare password
|
||||
const same = await argon2.verify(profile.password!, password) || bcrypt.compareSync(password, profile.password!);
|
||||
const same = await comparePassword(password, profile.password!);
|
||||
|
||||
if (same && isOldAlgorithm(profile.password!)) {
|
||||
profile.password = await hashPassword(password);
|
||||
await this.userProfilesRepository.save(profile);
|
||||
}
|
||||
|
||||
const fail = async (status?: number, failure?: { id: string }) => {
|
||||
// Append signin history
|
||||
@ -141,12 +145,6 @@ export class SigninApiService {
|
||||
|
||||
if (!profile.twoFactorEnabled) {
|
||||
if (same) {
|
||||
if (profile.password!.startsWith('$2')) {
|
||||
const newHash = await argon2.hash(password);
|
||||
this.userProfilesRepository.update(user.id, {
|
||||
password: newHash,
|
||||
});
|
||||
}
|
||||
return this.signinService.signin(request, reply, user);
|
||||
} else {
|
||||
return await fail(403, {
|
||||
@ -163,12 +161,6 @@ export class SigninApiService {
|
||||
}
|
||||
|
||||
try {
|
||||
if (profile.password!.startsWith('$2')) {
|
||||
const newHash = await argon2.hash(password);
|
||||
this.userProfilesRepository.update(user.id, {
|
||||
password: newHash,
|
||||
});
|
||||
}
|
||||
await this.userAuthService.twoFactorAuthenticate(profile, token);
|
||||
} catch (e) {
|
||||
return await fail(403, {
|
||||
|
@ -4,9 +4,8 @@
|
||||
*/
|
||||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
//import bcrypt from 'bcryptjs';
|
||||
import * as argon2 from 'argon2';
|
||||
import { IsNull } from 'typeorm';
|
||||
import { hashPassword } from '@/misc/password.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { RegistrationTicketsRepository, UsedUsernamesRepository, UserPendingsRepository, UserProfilesRepository, UsersRepository, MiRegistrationTicket, MiMeta } from '@/models/_.js';
|
||||
import type { Config } from '@/config.js';
|
||||
@ -180,8 +179,7 @@ export class SignupApiService {
|
||||
const code = secureRndstr(16, { chars: L_CHARS });
|
||||
|
||||
// Generate hash of password
|
||||
//const salt = await bcrypt.genSalt(8);
|
||||
const hash = await argon2.hash(password);
|
||||
const hash = await hashPassword(password);
|
||||
|
||||
const pendingUser = await this.userPendingsRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
|
@ -4,8 +4,7 @@
|
||||
*/
|
||||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
//import bcrypt from 'bcryptjs';
|
||||
import * as argon2 from 'argon2';
|
||||
import { hashPassword } from '@/misc/password.js';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import type { UsersRepository, UserProfilesRepository } from '@/models/_.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
@ -63,10 +62,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
throw new Error('cannot reset password of root');
|
||||
}
|
||||
|
||||
const passwd = secureRndstr(8);
|
||||
const passwd = secureRndstr(16);
|
||||
|
||||
// Generate hash of password
|
||||
const hash = await argon2.hash(passwd);
|
||||
const hash = await hashPassword(passwd);
|
||||
|
||||
await this.userProfilesRepository.update({
|
||||
userId: user.id,
|
||||
|
@ -3,8 +3,6 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
//import bcrypt from 'bcryptjs';
|
||||
import * as argon2 from 'argon2';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
@ -14,6 +12,7 @@ import type { UserProfilesRepository, UserSecurityKeysRepository } from '@/model
|
||||
import { WebAuthnService } from '@/core/WebAuthnService.js';
|
||||
import { ApiError } from '@/server/api/error.js';
|
||||
import { UserAuthService } from '@/core/UserAuthService.js';
|
||||
import { comparePassword } from '@/misc/password.js';
|
||||
|
||||
export const meta = {
|
||||
requireCredential: true,
|
||||
@ -86,7 +85,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
}
|
||||
}
|
||||
|
||||
const passwordMatched = await argon2.verify(profile.password ?? '', ps.password);
|
||||
const passwordMatched = await comparePassword(ps.password, profile.password ?? '');
|
||||
if (!passwordMatched) {
|
||||
throw new ApiError(meta.errors.incorrectPassword);
|
||||
}
|
||||
|
@ -3,8 +3,6 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
//import bcrypt from 'bcryptjs';
|
||||
import * as argon2 from 'argon2';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import type { UserProfilesRepository } from '@/models/_.js';
|
||||
@ -12,6 +10,7 @@ import { DI } from '@/di-symbols.js';
|
||||
import { WebAuthnService } from '@/core/WebAuthnService.js';
|
||||
import { ApiError } from '@/server/api/error.js';
|
||||
import { UserAuthService } from '@/core/UserAuthService.js';
|
||||
import { comparePassword } from '@/misc/password.js';
|
||||
|
||||
export const meta = {
|
||||
requireCredential: true,
|
||||
@ -217,7 +216,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
}
|
||||
}
|
||||
|
||||
const passwordMatched = await argon2.verify(profile.password ?? '', ps.password);
|
||||
const passwordMatched = await comparePassword(ps.password, profile.password ?? '');
|
||||
if (!passwordMatched) {
|
||||
throw new ApiError(meta.errors.incorrectPassword);
|
||||
}
|
||||
|
@ -3,8 +3,6 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
//import bcrypt from 'bcryptjs';
|
||||
import * as argon2 from 'argon2';
|
||||
import * as OTPAuth from 'otpauth';
|
||||
import * as QRCode from 'qrcode';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
@ -14,6 +12,7 @@ import { DI } from '@/di-symbols.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import { ApiError } from '@/server/api/error.js';
|
||||
import { UserAuthService } from '@/core/UserAuthService.js';
|
||||
import { comparePassword } from '@/misc/password.js';
|
||||
|
||||
export const meta = {
|
||||
requireCredential: true,
|
||||
@ -78,7 +77,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
}
|
||||
}
|
||||
|
||||
const passwordMatched = await argon2.verify(profile.password ?? '', ps.password);
|
||||
const passwordMatched = await comparePassword(ps.password, profile.password ?? '');
|
||||
if (!passwordMatched) {
|
||||
throw new ApiError(meta.errors.incorrectPassword);
|
||||
}
|
||||
|
@ -3,8 +3,6 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
//import bcrypt from 'bcryptjs';
|
||||
import * as argon2 from 'argon2';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import type { UserProfilesRepository, UserSecurityKeysRepository } from '@/models/_.js';
|
||||
@ -13,6 +11,7 @@ import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { ApiError } from '@/server/api/error.js';
|
||||
import { UserAuthService } from '@/core/UserAuthService.js';
|
||||
import { comparePassword } from '@/misc/password.js';
|
||||
|
||||
export const meta = {
|
||||
requireCredential: true,
|
||||
@ -67,7 +66,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
}
|
||||
}
|
||||
|
||||
const passwordMatched = await argon2.verify(profile.password ?? '', ps.password);
|
||||
const passwordMatched = await comparePassword(ps.password, profile.password ?? '');
|
||||
if (!passwordMatched) {
|
||||
throw new ApiError(meta.errors.incorrectPassword);
|
||||
}
|
||||
|
@ -3,8 +3,6 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
//import bcrypt from 'bcryptjs';
|
||||
import * as argon2 from 'argon2';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
@ -13,6 +11,7 @@ import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { ApiError } from '@/server/api/error.js';
|
||||
import { UserAuthService } from '@/core/UserAuthService.js';
|
||||
import { comparePassword } from '@/misc/password.js';
|
||||
|
||||
export const meta = {
|
||||
requireCredential: true,
|
||||
@ -63,7 +62,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
}
|
||||
}
|
||||
|
||||
const passwordMatched = await argon2.verify(profile.password ?? '', ps.password);
|
||||
const passwordMatched = await comparePassword(ps.password, profile.password ?? '');
|
||||
if (!passwordMatched) {
|
||||
throw new ApiError(meta.errors.incorrectPassword);
|
||||
}
|
||||
|
@ -3,13 +3,12 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
//import bcrypt from 'bcryptjs';
|
||||
import * as argon2 from 'argon2';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import type { UserProfilesRepository } from '@/models/_.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { UserAuthService } from '@/core/UserAuthService.js';
|
||||
import { hashPassword, comparePassword } from '@/misc/password.js';
|
||||
|
||||
export const meta = {
|
||||
requireCredential: true,
|
||||
@ -51,15 +50,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
}
|
||||
}
|
||||
|
||||
const passwordMatched = await argon2.verify(profile.password!, ps.currentPassword);
|
||||
const passwordMatched = await comparePassword(ps.currentPassword, profile.password!);
|
||||
|
||||
if (!passwordMatched) {
|
||||
throw new Error('incorrect password');
|
||||
}
|
||||
|
||||
// Generate hash of password
|
||||
//const salt = await bcrypt.genSalt(8);
|
||||
const hash = await argon2.hash(ps.newPassword);
|
||||
const hash = await hashPassword(ps.newPassword);
|
||||
|
||||
await this.userProfilesRepository.update(me.id, {
|
||||
password: hash,
|
||||
|
@ -3,14 +3,13 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
//import bcrypt from 'bcryptjs';
|
||||
import * as argon2 from 'argon2';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import type { UsersRepository, UserProfilesRepository } from '@/models/_.js';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import { DeleteAccountService } from '@/core/DeleteAccountService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { UserAuthService } from '@/core/UserAuthService.js';
|
||||
import { comparePassword } from '@/misc/password.js';
|
||||
|
||||
export const meta = {
|
||||
requireCredential: true,
|
||||
@ -60,7 +59,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
return;
|
||||
}
|
||||
|
||||
const passwordMatched = await argon2.verify(profile.password!, ps.password);
|
||||
const passwordMatched = await comparePassword(ps.password, profile.password!);
|
||||
if (!passwordMatched) {
|
||||
throw new Error('incorrect password');
|
||||
}
|
||||
|
@ -3,14 +3,13 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
//import bcrypt from 'bcryptjs';
|
||||
import * as argon2 from 'argon2';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import type { UsersRepository, UserProfilesRepository } from '@/models/_.js';
|
||||
import generateUserToken from '@/misc/generate-native-user-token.js';
|
||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { comparePassword } from '@/misc/password.js';
|
||||
|
||||
export const meta = {
|
||||
requireCredential: true,
|
||||
@ -44,7 +43,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id });
|
||||
|
||||
// Compare password
|
||||
const same = await argon2.verify(profile.password!, ps.password);
|
||||
const same = await comparePassword(ps.password, profile.password!);
|
||||
|
||||
if (!same) {
|
||||
throw new Error('incorrect password');
|
||||
|
@ -5,8 +5,7 @@
|
||||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import ms from 'ms';
|
||||
//import bcrypt from 'bcryptjs';
|
||||
import * as argon2 from 'argon2';
|
||||
import { comparePassword } from '@/misc/password.js';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import type { MiMeta, UserProfilesRepository } from '@/models/_.js';
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
@ -97,7 +96,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
}
|
||||
}
|
||||
|
||||
const passwordMatched = await argon2.verify(profile.password!, ps.password);
|
||||
const passwordMatched = await comparePassword(ps.password, profile.password!);
|
||||
if (!passwordMatched) {
|
||||
throw new ApiError(meta.errors.incorrectPassword);
|
||||
}
|
||||
|
@ -3,13 +3,13 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
//import bcrypt from 'bcryptjs';
|
||||
import * as argon2 from 'argon2';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import type { UserProfilesRepository, PasswordResetRequestsRepository } from '@/models/_.js';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { hashPassword } from '@/misc/password.js';
|
||||
import { secureRndstr } from '@/misc/secure-rndstr.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['reset password'],
|
||||
@ -54,8 +54,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||
}
|
||||
|
||||
// Generate hash of password
|
||||
//const salt = await bcrypt.genSalt(8);
|
||||
const hash = await argon2.hash(ps.password);
|
||||
const passwd = secureRndstr(16);
|
||||
const hash = await hashPassword(passwd);
|
||||
|
||||
await this.userProfilesRepository.update(req.userId, {
|
||||
password: hash,
|
||||
|
Loading…
Reference in New Issue
Block a user