.NET에서 디렉토리 및 파일 쓰기 권한 확인


80

내 .NET 2.0 응용 프로그램에서 디렉터리에 파일을 만들고 쓸 수있는 충분한 권한이 있는지 확인해야합니다. 이를 위해 파일을 생성하고 단일 바이트를 쓰려고 시도한 다음 권한이 있는지 테스트하기 위해 나중에 자신을 삭제하는 다음 기능이 있습니다.

확인하는 가장 좋은 방법은 실제로 발생하는 예외를 포착하여 실제로 시도하는 것이라고 생각했습니다. 나는 일반적인 Exception catch에 대해 특별히 만족스럽지 않습니다. 그래서 더 나은 방법이 있습니까?

private const string TEMP_FILE = "\\tempFile.tmp";

/// <summary>
/// Checks the ability to create and write to a file in the supplied directory.
/// </summary>
/// <param name="directory">String representing the directory path to check.</param>
/// <returns>True if successful; otherwise false.</returns>
private static bool CheckDirectoryAccess(string directory)
{
    bool success = false;
    string fullPath = directory + TEMP_FILE;

    if (Directory.Exists(directory))
    {
        try
        {
            using (FileStream fs = new FileStream(fullPath, FileMode.CreateNew, 
                                                            FileAccess.Write))
            {
                fs.WriteByte(0xff);
            }

            if (File.Exists(fullPath))
            {
                File.Delete(fullPath);
                success = true;
            }
        }
        catch (Exception)
        {
            success = false;
        }
    }

코드 감사합니다.하지만 한 가지는 사용자가 쓸 수는 있지만 삭제할 수 없으면 쓰기 권한이 없다는 잘못된 인상을받을 수 있습니다. FileMode.Create를 사용하여 파일 삭제를 제거하도록 변경합니다. 분명히이 코드는 더 이상 필요하지 않지만 미래의 독자를 위해이 코드를 작성합니다.
n00b

3
string fullPath = directory + TEMP_FILE;fullPath를 얻으려면 문자열을 연결하는 대신 Path.Combine 메서드를 사용하십시오. Path.Combine(directory, TEMP_FILE)
nomail

누군가가 주먹을 찔렀다가 다음날 펀치를 날리면 어떨까요? 그들이 펀치를 날렸다가 이틀 후에 펀치를 날리면 어떨까요? 사람들이 그런 일을해서는 안된다고 확신하지만 행동은 정의되어야합니다.
Scott Hannen

답변:


23

RichardJason 의 대답 은 일종의 올바른 방향입니다. 그러나해야 할 일은 코드를 실행하는 사용자 ID에 대한 유효 권한계산 하는 것입니다. 예를 들어 위의 예 중 어느 것도 그룹 멤버십을 올바르게 설명하지 않습니다.

Keith Brown 이 .NET Developers Guide to Windows Security의 위키 버전 (현재 오프라인)에서이 작업을 수행하는 코드가 있다고 확신 합니다. 이것은 그의 프로그래밍 Windows 보안 책 에서도 합리적으로 자세히 설명 합니다.

효과적인 권한을 계산하는 것은 희미한 마음이 아니며 코드가 파일을 만들고 throw 된 보안 예외를 잡으려는 시도는 아마도 저항이 가장 적은 경로 일 것입니다.


2
그렇지 않으면 누군가가 확인하고 실제로 저장을 시도하는 사이에 권한을 변경할 수 있기 때문에 신뢰할 수있는 유일한 방법이기도합니다.
Chris Chilvers

1
감사합니다. 따라서 내 코드에 대해해야 할 유일한 변경 사항은 일반적인 '예외'대신 보안 예외를 포착하는 것입니다.
Andy

@Andy-예, 효과적인 권한을 계산하는 코드를 작성하지 않는 한 저항이 가장 적은 경로입니다.
Kev

2
왜 모든 것이 그렇게 복잡해야합니까!
Vidar 2011

3
@Triynko-내가 인용 한 기사를 읽어 보시기 바랍니다 : groups.google.com/group/…- 효과적인 권한 계산 은 생각만큼 간단하지 않습니다. 내 손님이되어 내가 틀렸다는 것을 증명하기 위해 대답을 제공하십시오.
Kev

49

Directory.GetAccessControl(path) 당신이 요구하는 것을합니다.

public static bool HasWritePermissionOnDir(string path)
{
    var writeAllow = false;
    var writeDeny = false;
    var accessControlList = Directory.GetAccessControl(path);
    if (accessControlList == null)
        return false;
    var accessRules = accessControlList.GetAccessRules(true, true, 
                                typeof(System.Security.Principal.SecurityIdentifier));
    if (accessRules ==null)
        return false;

    foreach (FileSystemAccessRule rule in accessRules)
    {
        if ((FileSystemRights.Write & rule.FileSystemRights) != FileSystemRights.Write) 
            continue;

        if (rule.AccessControlType == AccessControlType.Allow)
            writeAllow = true;
        else if (rule.AccessControlType == AccessControlType.Deny)
            writeDeny = true;
    }

    return writeAllow && !writeDeny;
}

(FileSystemRights.Write & rights) == FileSystemRights.Write "Flags"btw라는 것을 사용하고 있는데 이것이 무엇인지 모른다면 실제로 읽어야합니다. :)


6
물론 디렉터리에서 실제로 ACL을 가져올 수없는 경우 예외가 발생합니다.
blowdart

3
무엇을 확인합니까? 해당 디렉토리에는 쓰기 권한이 있지만 어떤 사용자에 대해? :)
Ivan G.

2
현재 사용자에게 쓰기 권한이 있는지 확인하려는 경우에만 작동합니다.
Donny V.

@aloneguid : "GetAccessRules"메서드는 AuthorizationRuleCollection을 반환합니다. AthorizationRule 클래스에는 실제로 런타임 유형이 IdenityReference 유형 (NTAccount 또는 보안)에서 파생되는 두 가지 중 하나 인 IdentityReference 속성이 있으며, 이는 GetAccessRules에 대한 호출에 지정되어 있습니다. IdentityReference 인스턴스 (또는 파생 된 유형)를 통해 규칙이 적용되는 사용자를 찾을 수 있습니다. SID 또는 NTAccount 이름 형식입니다.
Triynko 2011

7
관리자가 아닌 응용 프로그램을 사용하여 Windows 7의 시스템 디스크에서 실행하면 true가 반환되지만 c : \에 쓰려고하면 액세스 권한이 없다는 예외가 발생합니다!
Peter

34

Deny보다 우선합니다 Allow. 로컬 규칙은 상속 된 규칙보다 우선합니다. 여기에 표시된 일부 답변을 포함하여 많은 솔루션을 보았지만 그중 어느 것도 규칙이 상속 되는지 여부를 고려 하지 않습니다. 따라서 규칙 상속 (클래스로 깔끔하게 래핑 됨)을 고려하는 다음 접근 방식을 제안합니다.

public class CurrentUserSecurity
{
    WindowsIdentity _currentUser;
    WindowsPrincipal _currentPrincipal;

    public CurrentUserSecurity()
    {
        _currentUser = WindowsIdentity.GetCurrent();
        _currentPrincipal = new WindowsPrincipal(_currentUser);
    }

    public bool HasAccess(DirectoryInfo directory, FileSystemRights right)
    {
        // Get the collection of authorization rules that apply to the directory.
        AuthorizationRuleCollection acl = directory.GetAccessControl()
            .GetAccessRules(true, true, typeof(SecurityIdentifier));
        return HasFileOrDirectoryAccess(right, acl);
    }

    public bool HasAccess(FileInfo file, FileSystemRights right)
    {
        // Get the collection of authorization rules that apply to the file.
        AuthorizationRuleCollection acl = file.GetAccessControl()
            .GetAccessRules(true, true, typeof(SecurityIdentifier));
        return HasFileOrDirectoryAccess(right, acl);
    }

    private bool HasFileOrDirectoryAccess(FileSystemRights right,
                                          AuthorizationRuleCollection acl)
    {
        bool allow = false;
        bool inheritedAllow = false;
        bool inheritedDeny = false;

        for (int i = 0; i < acl.Count; i++) {
            var currentRule = (FileSystemAccessRule)acl[i];
            // If the current rule applies to the current user.
            if (_currentUser.User.Equals(currentRule.IdentityReference) ||
                _currentPrincipal.IsInRole(
                                (SecurityIdentifier)currentRule.IdentityReference)) {

                if (currentRule.AccessControlType.Equals(AccessControlType.Deny)) {
                    if ((currentRule.FileSystemRights & right) == right) {
                        if (currentRule.IsInherited) {
                            inheritedDeny = true;
                        } else { // Non inherited "deny" takes overall precedence.
                            return false;
                        }
                    }
                } else if (currentRule.AccessControlType
                                                  .Equals(AccessControlType.Allow)) {
                    if ((currentRule.FileSystemRights & right) == right) {
                        if (currentRule.IsInherited) {
                            inheritedAllow = true;
                        } else {
                            allow = true;
                        }
                    }
                }
            }
        }

        if (allow) { // Non inherited "allow" takes precedence over inherited rules.
            return true;
        }
        return inheritedAllow && !inheritedDeny;
    }
}

그러나 원격 컴퓨터에서는 항상 파일 액세스 권한을 쿼리 할 수있는 권한이 없기 때문에 이것이 항상 작동하는 것은 아니라는 경험을했습니다. 이 경우 해결책은 시도하는 것입니다. "실제"파일로 작업하기 직전에 액세스 권한을 알아야하는 경우 임시 파일을 만들려고해도 가능합니다.


2
나는이 답변이 그것을 달성하는 가장 좋은 방법이라고 생각하고 다른 답변도 같은 방법을 사용하여 결과를 얻지만이 답변 만 상속 된 규칙과 로컬 규칙을 계산하기 때문에 가장 정확한 것입니다. 감사합니다 & 축하합니다.
톨가 Evcimen

19

이 질문에 대한 Kev의 대답은 실제로 코드를 제공하는 것이 아니라 내가 액세스 할 수없는 다른 리소스를 가리 킵니다. 그래서 여기에 기능에 대한 최선의 시도가 있습니다. 실제로보고있는 권한이 "쓰기"권한이고 현재 사용자가 적절한 그룹에 속하는지 확인합니다.

네트워크 경로 또는 기타 사항과 관련하여 완전하지 않을 수 있지만 쓰기 가능성을 위해 "프로그램 파일"아래의 로컬 구성 파일을 확인하여 내 목적에는 충분합니다.

using System.Security.Principal;
using System.Security.AccessControl;

private static bool HasWritePermission(string FilePath)
{
    try
    {
        FileSystemSecurity security;
        if (File.Exists(FilePath))
        {
            security = File.GetAccessControl(FilePath);
        }
        else
        {
            security = Directory.GetAccessControl(Path.GetDirectoryName(FilePath));
        }
        var rules = security.GetAccessRules(true, true, typeof(NTAccount));

        var currentuser = new WindowsPrincipal(WindowsIdentity.GetCurrent());
        bool result = false;
        foreach (FileSystemAccessRule rule in rules)
        {
            if (0 == (rule.FileSystemRights &
                (FileSystemRights.WriteData | FileSystemRights.Write)))
            {
                continue;
            }

            if (rule.IdentityReference.Value.StartsWith("S-1-"))
            {
                var sid = new SecurityIdentifier(rule.IdentityReference.Value);
                if (!currentuser.IsInRole(sid))
                {
                    continue;
                }
            }
            else
            {
                if (!currentuser.IsInRole(rule.IdentityReference.Value))
                {
                    continue;
                }
            }

            if (rule.AccessControlType == AccessControlType.Deny)
                return false;
            if (rule.AccessControlType == AccessControlType.Allow)
                result = true;
        }
        return result;
    }
    catch
    {
        return false;
    }
}

이것은 그룹에서는 작동하지 않지만 내 경우에만 문자 그대로 추가 된 계정 이름에 대해 작동합니다
Random

그렇다면 이것은 "(S-1-5-21-397955417-626881126-188441444-512)"유형 형식과 관련이 있습니까? 문자열을 SecurityIdentifier로 변환하면 문제가 해결 되었습니까? 귀하의 의견에서 그것이 귀하에게 지금 작동하는지 여부는 명확하지 않습니다.
Bryce Wagner

currentuser.IsInRole ()의 매개 변수로 "rule.IdentityReference.Value"를 입력 할 때 일반 "domain \ user"값으로 일치를 시도하는 IsInRole (string) 메소드를 사용합니다. 따라서 사용자 이름 문자열 대신 SID 문자열을 푸시하고 있습니다. 그러나 그 앞에 내 줄을 사용하면 주어진 SID의 사용자와 일치하는 SecurityIdentifier 개체를 얻을 수 있습니다. 이 "문자열"인수 오버로드는 개발자에게 작은 함정입니다. 다시 한 번 SID 문자열 표현이 아닌 사람이 다시 사용할 수있는 형식의 계정 또는 그룹 이름을 허용합니다.
무작위

문제는 "new SecurityIdentifier (SDDLFormat)"가 일반 그룹 이름에서 작동하지 않는다는 것입니다 (인수 예외가 발생 함). 그래서 SDDL 형식인지 확인했습니다.
Bryce Wagner

2
이 솔루션은 저에게 효과적이지만 네트워크 폴더에 한 가지 문제가 있습니다. 폴더에에 쓰기를 허용하는 액세스 규칙이 있습니다 BUILTIN\Administrators. 그리고 내가 내 지역 스테이션의 관리자이기 때문에 코드 조각이 실수로 true.
Ilia Barahovski

5

IMO에서는 평소와 같이 이러한 디렉터리로 작업해야하지만 사용 전에 권한을 확인하는 대신 UnauthorizedAccessException을 처리하고 그에 따라 대응하는 올바른 방법을 제공합니다. 이 방법은 더 쉽고 오류 발생 가능성이 훨씬 적습니다.


1
아마도 '이 방법은 더 쉽고 오류가 발생합니다.' 라고 말하고 싶었을 것입니다 .
cjbarth

3

방금 만든이 C # 스 니펫으로 작업 해보세요.

using System;
using System.IO;
using System.Security.AccessControl;
using System.Security.Principal;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string directory = @"C:\downloads";

            DirectoryInfo di = new DirectoryInfo(directory);

            DirectorySecurity ds = di.GetAccessControl();

            foreach (AccessRule rule in ds.GetAccessRules(true, true, typeof(NTAccount)))
            {
                Console.WriteLine("Identity = {0}; Access = {1}", 
                              rule.IdentityReference.Value, rule.AccessControlType);
            }
        }
    }
}

그리고 여기 당신이 볼 수있는 참고 자료가 있습니다. 내 코드는 디렉토리에 쓰기를 시도하기 전에 권한을 확인하는 방법에 대한 아이디어를 줄 수 있습니다.


typeof객체의 유형을 반환합니다 (이 경우) NTAccount. docs.microsoft.com/en-us/dotnet/csharp/language-reference/…에 대한 호출에는 호출 GetAccessRules()시 계정 유형이 필요합니다. msdn.microsoft.com/en-us/library/…
Jason Evans

왜 사용 NTAccount합니까? 항상 NTAccount를 사용 합니까?
Kiquenet

이 경우에는 그렇습니다. NTAccountWindows PC의 사용자 계정을 나타내므로 위 코드에서 필요합니다.
Jason Evans

1

정적 메서드 'GetAccessControl'이 현재 버전의 .Net core / Standard에서 누락 된 것으로 보이므로 @Bryce Wagner의 답변을 수정해야했습니다 (계속 진행하여 더 현대적인 구문을 사용했습니다).

public static class PermissionHelper
{
  public static bool? CurrentUserHasWritePermission(string filePath)

     => new WindowsPrincipal(WindowsIdentity.GetCurrent())
        .SelectWritePermissions(filePath)
        .FirstOrDefault();


  private static IEnumerable<bool?> SelectWritePermissions(this WindowsPrincipal user, string filePath)
     => from rule in filePath
                    .GetFileSystemSecurity()
                    .GetAccessRules(true, true, typeof(NTAccount))
                    .Cast<FileSystemAccessRule>()
        let right = user.HasRightSafe(rule)
        where right.HasValue
        // Deny takes precedence over allow
        orderby right.Value == false descending
        select right;


  private static bool? HasRightSafe(this WindowsPrincipal user, FileSystemAccessRule rule)
  {
     try
     {
        return user.HasRight(rule);
     }
     catch
     {
        return null;
     }
  }

  private static bool? HasRight(this WindowsPrincipal user,FileSystemAccessRule rule )
     => rule switch
     {
        { FileSystemRights: FileSystemRights fileSystemRights } when (fileSystemRights &
                                                                      (FileSystemRights.WriteData | FileSystemRights.Write)) == 0 => null,
        { IdentityReference: { Value: string value } } when value.StartsWith("S-1-") &&
                                                            !user.IsInRole(new SecurityIdentifier(rule.IdentityReference.Value)) => null,
        { IdentityReference: { Value: string value } } when value.StartsWith("S-1-") == false &&
                                                            !user.IsInRole(rule.IdentityReference.Value) => null,
        { AccessControlType: AccessControlType.Deny } => false,
        { AccessControlType: AccessControlType.Allow } => true,
        _ => null
     };


  private static FileSystemSecurity GetFileSystemSecurity(this string filePath)
    => new FileInfo(filePath) switch
    {
       { Exists: true } fileInfo => fileInfo.GetAccessControl(),
       { Exists: false } fileInfo => (FileSystemSecurity)fileInfo.Directory.GetAccessControl(),
       _ => throw new Exception($"Check the file path, {filePath}: something's wrong with it.")
    };
}

0

이 링크에 따르면 : http://www.authorcode.com/how-to-check-file-permission-to-write-in-c/

기존 클래스 SecurityManager를 사용하는 것이 더 쉽습니다.

string FileLocation = @"C:\test.txt";
FileIOPermission writePermission = new FileIOPermission(FileIOPermissionAccess.Write, FileLocation);
if (SecurityManager.IsGranted(writePermission))
{
  // you have permission
}
else
{
 // permission is required!
}

그러나 더 이상 사용되지 않는 것 같습니다. 대신 PermissionSet을 사용하는 것이 좋습니다.

[Obsolete("IsGranted is obsolete and will be removed in a future release of the .NET Framework.  Please use the PermissionSet property of either AppDomain or Assembly instead.")]

-1
private static void GrantAccess(string file)
        {
            bool exists = System.IO.Directory.Exists(file);
            if (!exists)
            {
                DirectoryInfo di = System.IO.Directory.CreateDirectory(file);
                Console.WriteLine("The Folder is created Sucessfully");
            }
            else
            {
                Console.WriteLine("The Folder already exists");
            }
            DirectoryInfo dInfo = new DirectoryInfo(file);
            DirectorySecurity dSecurity = dInfo.GetAccessControl();
            dSecurity.AddAccessRule(new FileSystemAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), FileSystemRights.FullControl, InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit, PropagationFlags.NoPropagateInherit, AccessControlType.Allow));
            dInfo.SetAccessControl(dSecurity);

        }

WellKnownSidType.WorldSid 는 무엇입니까 ?
Kiquenet

@Kiquenet WorldSid는 "Everyone"빌드 그룹입니다.
AussieALF
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.