Asp.Net Core Identity 프라이버시 데이터 보호의 실현
Asp.Net Core Identity 는 Asp.Net Core 의 중요 한 구성 부분 으로 그 는 Asp.Net Core 심지어 다른.Net Core 응용 프로그램 에 간단 하고 사용 하기 쉬 우 며 확장 하기 쉬 운 기초 사용자 관리 시스템 프레임 워 크 를 제공 했다.기본 적 인 사용자,캐릭터,제3자 로그 인,Claim 등의 기능 을 포함 하고 있 으 며,Identity Server 4 를 사용 하면 OpenId connection 과 Oauth 2.0 관련 기능 을 쉽게 확장 할 수 있다.인터넷 에 이미 대량의 관련 글 이 소개 되 었 지만 이것 은 Asp.Net Core Identity 의 전부 가 아니 라 그 중 하 나 는 바로 프라이버시 데이터 보호 이다.
본문
얼핏 보면 프라이버시 데이터 보호 가 뭔 지 알 것 같 지만 잘 모 르 겠 어 요.확실히 이 물건 은 말 만 으로 는 설명 하기 어렵다.그러면 바로 그림 을 올 려 라.
이것 은 사용자 표 의 일부분 입 니 다.문제점 을 발견 하 셨 습 니까?사용자 이름과 이메일 필드 는 알 아 볼 수 없 는 것들 로 변 했다.자세히 보면 이 문자열 은 규칙 이 있 는 것 같 습 니 다.guid+사칭+base 64 인 코딩 문자열 인 것 같 습 니 다.물론 이 문자열 은 온라인 디 코딩 을 하 는 결과 가 어 지 러 운 것 같 습 니 다.예 를 들 어 id 가 1 인 Username:svBqhhluYZSiPZVUF4baOQ=온라인 디 코딩 후²ðj†na”¢=•T†Ú9 。
이것 이 바로 프라이버시 데이터 보호 입 니 다.만약 에 이 기능 이 없 으 면 사용자 이름 은 명문 으로 저 장 됩 니 다.비밀 번 호 는 hash 로 풀 기 어렵 지만 라 이브 러 리 에 끌 리 면 사용자 데이터 도 더욱 큰 위험 에 직면 할 것 입 니 다.서로 다른 사이트 에서 같은 계 정 정 정 보 를 이용 해 등록 하 는 것 을 선 호 해 잊 혀 지지 않 기 때문이다.한 사이트 의 비밀번호 가 도 둑 맞 고 다른 사이트 가 라 이브 러 리 로 끌 리 면 해커 는 같은 사용자 이름 이 있 는 지 비교 해 라 이브 러 리 충돌 을 시도 할 수 있 고,심지어 이메일 이 도 둑 맞 으 면 해커 는 이메일 이 비밀 번 호 를 찾 아 NTR 에 계 정 을 주 는 것 도 지 켜 볼 수 있다.한편,프라이버시 데이터 보 호 는 더욱 튼튼한 뒷받침 이다.라 이브 러 리 에 끌 려 도 해커 는 안에 있 는 것 을 알 아 보지 못 한다.
그 다음 에 이 형식 은 대체적으로 생각 할 수 있 습 니 다.짝 퉁 은 구분자 이 고 앞 에 있 는 guid 이 며 뒤 에는 암호 화 된 내용 입 니 다.그 문 제 는 guid 가 되 고 또 뭐 하 는 거 야?그냥 암호 화 된 내용 을 저장 하면 되 잖 아.이것 은 마이크로소프트 개발 프레임 워 크 가 디 테 일 을 중시 하 는 가장 좋 은 표현 이 고 그 다음 에 코드 를 결합 하면 결말 을 알 수 있다.
프라이버시 데이터 보호 사용
// Identity ( EF , EF )
services.AddIdentity<ApplicationUser, ApplicationRole>(options =>
{
//...
options.Stores.ProtectPersonalData = true; //
})
//...
.AddPersonalDataProtection<AesProtector, AesProtectorKeyRing>(); // , , ,
그 중의 AesProtector 와 AesProtector KeyRing 은 스스로 실현 해 야 한다.마이크로소프트 는 기 존의 종 류 를 제공 하지 않 았 다.적어도 나 는 찾 지 못 했다.아마도 이 기능 이 인기 가 없 는 원인 일 것 이다.Neter 는 마이크로소프트 에 의 해 버릇 이 나 빠 져 서 모두 옷 으로 손 을 내밀 고 밥 으로 입 을 벌 렸 다.AesProtector KeyRing 에 KeyRing 이라는 글자 가 있 는 것 을 발 견 했 습 니까?열쇠 꾸러미,맞 힌 것 을 축하합니다.guid 가 바로 이 열쇠 꾸러미 의 열쇠 번호 입 니 다.암호 화 된 열쇠 가 도 둑 맞 았 지만 모두 도 둑 맞 은 것 이 아니라면 사용자 정보 가 모두 유출 되 지 는 않 는 다 는 것 이다.마이크로소프트 의 이 수단 은 정말 잔인 하 다!다음은 이 두 종류 가 무엇 인지 보 자.
AesProtector 는 ILookupProtector 의 실현 이다.인 터 페 이 스 는 암호 화 와 복호화 에 사용 되 는 두 가지 방법 을 포함 하고 문자열 을 되 돌려 줍 니 다.매개 변 수 는 문자열 데이터 와 위 에 있 는 guid 를 포함 합 니 다.물론 실제 문자열 이면 됩 니 다.guid 는 제 개인 적 인 선택 입 니 다.중복 되 지 않 는 문자열 을 만 드 는 것 이 편리 합 니까?아니면 guid 를 만 드 는 것 이 편리 합 니까?
AesProtectorKeyRing 은 ILookupProtectorKeyRing 의 실현 이다.인 터 페 이 스 는 현재 사용 하고 있 는 열쇠 번호 의 읽 기 전용 속성 을 포함 하여 암호 화 열 쇠 를 제공 합 니 다.2.열쇠 번호 에 따라 문자열 의 색인 기 를 가 져 옵 니 다.3.모든 열쇠 번 호 를 얻 는 방법.
AesProtector
class AesProtector : ILookupProtector
{
private readonly object _locker;
private readonly Dictionary<string, SecurityUtil.AesProtector> _protectors;
private readonly DirectoryInfo _dirInfo;
public AesProtector(IWebHostEnvironment environment)
{
_locker = new object();
_protectors = new Dictionary<string, SecurityUtil.AesProtector>();
_dirInfo = new DirectoryInfo($@"{environment.ContentRootPath}\App_Data\AesDataProtectionKey");
}
public string Protect(string keyId, string data)
{
if (data.IsNullOrEmpty())
{
return data;
}
CheckOrCreateProtector(keyId);
return _protectors[keyId].Protect(Encoding.UTF8.GetBytes(data)).ToBase64String();
}
public string Unprotect(string keyId, string data)
{
if (data.IsNullOrEmpty())
{
return data;
}
CheckOrCreateProtector(keyId);
return Encoding.UTF8.GetString(_protectors[keyId].Unprotect(data.ToBytesFromBase64String()));
}
private void CheckOrCreateProtector(string keyId)
{
if (!_protectors.ContainsKey(keyId))
{
lock (_locker)
{
if (!_protectors.ContainsKey(keyId))
{
var fileInfo = _dirInfo.GetFiles().FirstOrDefault(d => d.Name == $@"key-{keyId}.xml") ??
throw new FileNotFoundException();
using (var stream = fileInfo.OpenRead())
{
XDocument xmlDoc = XDocument.Load(stream);
_protectors.Add(keyId,
new SecurityUtil.AesProtector(xmlDoc.Element("key")?.Element("encryption")?.Element("masterKey")?.Value.ToBytesFromBase64String()
, xmlDoc.Element("key")?.Element("encryption")?.Element("iv")?.Value.ToBytesFromBase64String()
, int.Parse(xmlDoc.Element("key")?.Element("encryption")?.Attribute("BlockSize")?.Value)
, int.Parse(xmlDoc.Element("key")?.Element("encryption")?.Attribute("KeySize")?.Value)
, int.Parse(xmlDoc.Element("key")?.Element("encryption")?.Attribute("FeedbackSize")?.Value)
, Enum.Parse<PaddingMode>(xmlDoc.Element("key")?.Element("encryption")?.Attribute("Padding")?.Value)
, Enum.Parse<CipherMode>(xmlDoc.Element("key")?.Element("encryption")?.Attribute("Mode")?.Value)));
}
}
}
}
}
}
AesProtectorKeyRing
class AesProtectorKeyRing : ILookupProtectorKeyRing
{
private readonly object _locker;
private readonly Dictionary<string, XDocument> _keyRings;
private readonly DirectoryInfo _dirInfo;
public AesProtectorKeyRing(IWebHostEnvironment environment)
{
_locker = new object();
_keyRings = new Dictionary<string, XDocument>();
_dirInfo = new DirectoryInfo($@"{environment.ContentRootPath}\App_Data\AesDataProtectionKey");
ReadKeys(_dirInfo);
}
public IEnumerable<string> GetAllKeyIds()
{
return _keyRings.Keys;
}
public string CurrentKeyId => NewestActivationKey(DateTimeOffset.Now)?.Element("key")?.Attribute("id")?.Value ?? GenerateKey(_dirInfo)?.Element("key")?.Attribute("id")?.Value;
public string this[string keyId] =>
GetAllKeyIds().FirstOrDefault(id => id == keyId) ?? throw new KeyNotFoundException();
private void ReadKeys(DirectoryInfo dirInfo)
{
foreach (var fileInfo in dirInfo.GetFiles().Where(f => f.Extension == ".xml"))
{
using (var stream = fileInfo.OpenRead())
{
XDocument xmlDoc = XDocument.Load(stream);
_keyRings.TryAdd(xmlDoc.Element("key")?.Attribute("id")?.Value, xmlDoc);
}
}
}
private XDocument GenerateKey(DirectoryInfo dirInfo)
{
var now = DateTimeOffset.Now;
if (!_keyRings.Any(item =>
DateTimeOffset.Parse(item.Value.Element("key")?.Element("activationDate")?.Value) <= now
&& DateTimeOffset.Parse(item.Value.Element("key")?.Element("expirationDate")?.Value) > now))
{
lock (_locker)
{
if (!_keyRings.Any(item =>
DateTimeOffset.Parse(item.Value.Element("key")?.Element("activationDate")?.Value) <= now
&& DateTimeOffset.Parse(item.Value.Element("key")?.Element("expirationDate")?.Value) > now))
{
var masterKeyId = Guid.NewGuid().ToString();
XDocument xmlDoc = new XDocument();
xmlDoc.Declaration = new XDeclaration("1.0", "utf-8", "yes");
XElement key = new XElement("key");
key.SetAttributeValue("id", masterKeyId);
key.SetAttributeValue("version", 1);
XElement creationDate = new XElement("creationDate");
creationDate.SetValue(now);
XElement activationDate = new XElement("activationDate");
activationDate.SetValue(now);
XElement expirationDate = new XElement("expirationDate");
expirationDate.SetValue(now.AddDays(90));
XElement encryption = new XElement("encryption");
encryption.SetAttributeValue("BlockSize", 128);
encryption.SetAttributeValue("KeySize", 256);
encryption.SetAttributeValue("FeedbackSize", 128);
encryption.SetAttributeValue("Padding", PaddingMode.PKCS7);
encryption.SetAttributeValue("Mode", CipherMode.CBC);
SecurityUtil.AesProtector protector = new SecurityUtil.AesProtector();
XElement masterKey = new XElement("masterKey");
masterKey.SetValue(protector.GenerateKey().ToBase64String());
XElement iv = new XElement("iv");
iv.SetValue(protector.GenerateIV().ToBase64String());
xmlDoc.Add(key);
key.Add(creationDate);
key.Add(activationDate);
key.Add(expirationDate);
key.Add(encryption);
encryption.Add(masterKey);
encryption.Add(iv);
xmlDoc.Save(
$@"{dirInfo.FullName}\key-{masterKeyId}.xml");
_keyRings.Add(masterKeyId, xmlDoc);
return xmlDoc;
}
return NewestActivationKey(now);
}
}
return NewestActivationKey(now);
}
private XDocument NewestActivationKey(DateTimeOffset now)
{
return _keyRings.Where(item =>
DateTimeOffset.Parse(item.Value.Element("key")?.Element("activationDate")?.Value) <= now
&& DateTimeOffset.Parse(item.Value.Element("key")?.Element("expirationDate")?.Value) > now)
.OrderByDescending(item =>
DateTimeOffset.Parse(item.Value.Element("key")?.Element("expirationDate")?.Value)).FirstOrDefault().Value;
}
}
이 두 종류 도 Asp.Net Core DI 에 등 록 된 서비스 로 모든 DI 기능 이 지원 된다.그 중에서 저 는 제 가 다른 곳 에서 쓴 바 텀 기초 도구 류 도 사 용 했 습 니 다.완 성 된 실현 을 보고 싶 으 면 제 Github 복제 코드 를 실제로 실행 하고 체험 할 수 있 습 니 다.여기 서 이 두 가지 유형의 디자인 방향 을 대충 말 해 보 세 요.마이크로소프트 가 열쇠 고리 기능 을 설계 한 이상 당연히 잘 이용 해 야 한다.나 는 코드 에 모든 열쇠 의 유효기간 90 일 을 적 었 다.기한 이 지나 면 자동 으로 새로운 열 쇠 를 생 성하 고 사용 할 것 이다.열쇠 의 상세 한 정 보 는 xml 문 서 를 사용 하여 프로젝트 폴 더 에 저장 하고 아래 의 캡 처 를 구체 적 으로 볼 수 있다.Identity 는 최신 열 쇠 를 사용 하여 암호 화하 고 열쇠 번 호 를 데이터베이스 에 저장 하 며 읽 을 때 번호 에 따라 해당 하 는 암호 기 복호화 데 이 터 를 찾 습 니 다.이 과정 은 EF Core 의 값 변환기(EF Core 2.1 증가)로 이 뤄 졌 으 며,Identity 가 DbContext 에서 암호 화해 야 할 필드 에 값 변환 기 를 등록 한 것 이다.그래서 저도 초기 에 Identity 에 이런 기능 이 있 었 는 지,EF Core 를 사용 하지 않 은 상황 에서 이 기능 이 사용 가능 한 지 잘 모 르 겠 습 니 다.
사용자 정의 데 이 터 를 보호 하려 면 속성 에[PersonalData]특성 을 표시 하면 됩 니 다.Identity 는 위 에서 언급 한 UserName 과 같은 내부 의 일부 속성 을 표시 했다.
특별히 주의해 야 할 몇 가지 점 이 있다.
1.데이터 가 있 는 상황 에서 데이터 보호 기능 을 함부로 켜 거나 닫 지 마 십시오.그렇지 않 으 면 심각 한 결 과 를 초래 할 수 있 습 니 다.
2.열 쇠 는 반드시 잘 보호 하고 잘 보관 해 야 합 니 다.그렇지 않 으 면 사용자 데 이 터 를 누설 하거나 더 이상 사용자 데 이 터 를 복호화 할 수 없 을 수도 있 습 니 다.라 이브 러 리 삭제 부터 도망 가 는 시 프 트+Del 까지 절대 하지 마 세 요.
3.보 호 된 필드 는 데이터베이스 에서 모호 한 검색 을 수행 할 수 없고 정확하게 일치 할 수 있 습 니 다.데이터 분석 을 하려 면 Identity 로 데 이 터 를 메모리 에 읽 어야 다른 일 을 계속 할 수 있 습 니 다.
4.열쇠 의 유효기간 이 너무 짧 으 면 안 됩 니 다.사용자 가 로그 인 할 때 Identity 는 사용자 가 언제 등 록 했 는 지,어떤 열 쇠 를 사용 해 야 하 는 지 모 르 기 때문에 Identity 는 모든 열쇠 로 암호 화한 다음 에 정확 한 일치 기록 이 있 는 지 찾 습 니 다.열쇠 의 유효기간 이 짧 을 수록 사이트 운영 시간 이 늘 어 나 면서 열쇠 의 수가 늘 어 나 고 시도 해 야 할 열쇠 도 늘 어 나 시스템 성능 에 영향 을 미친다.물론 캐 시 로 풀 수 있다.
효과 미리 보기:
본문 주소:https://www.cnblogs.com/coredx/p/12210232.html
전체 소스 코드:Github
안에 여러 가지 작은 것들 이 있 는데,이것 은 그 중의 하나 일 뿐,싫어 하지 않 는 다 면 스타 해 볼 수 있다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
WebAPI 원본 POST 요청 BODY 의 JSON 내용 가 져 오기controller 의 action 에 다음 코드 를 쓰 십시오: 그러나 이 코드 에서 얻 은 콘 텐 츠 는 비어 있 었 고 한참 동안 알 지 못 했 으 며 나중에 한 외국 포럼 에서 원인 을 찾 았 다. 그 이 유...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.