Acesso negado ao HttpListener

180

Estou escrevendo um servidor HTTP em c #.

Quando tento executar a função HttpListener.Start(), recebo um HttpListenerExceptionditado

"Acesso negado".

Quando executo o aplicativo no modo de administrador no Windows 7, ele funciona bem.

Posso fazê-lo funcionar sem o modo de administrador? se sim como? Caso contrário, como posso fazer o aplicativo mudar para o modo de administrador depois de começar a executar?

using System;
using System.Net;

namespace ConsoleApplication1
{
    class Program
    {
        private HttpListener httpListener = null;

        static void Main(string[] args)
        {
            Program p = new Program();
            p.Server();
        }

        public void Server()
        {
            this.httpListener = new HttpListener();

            if (httpListener.IsListening)
                throw new InvalidOperationException("Server is currently running.");

            httpListener.Prefixes.Clear();
            httpListener.Prefixes.Add("http://*:4444/");

            try
            {
                httpListener.Start(); //Throws Exception
            }
            catch (HttpListenerException ex)
            {
                if (ex.Message.Contains("Access is denied"))
                {
                    return;
                }
                else
                {
                    throw;
                }
            }
        }
    }
}
Randall Flagg
fonte
Se alguém quiser evitar esse erro, ele pode tentar escrevê-lo com o TcpListener. Ele não exige privilégios de administrador
Vlad
Eu enfrento o mesmo problema, no Visual Studio 2008 + Windows 7, ele produz um erro 'Acesso negado', para resolver isso, é executar o Visual Studio 2008 no modo Admin
Mark Khor

Respostas:

307

Sim, você pode executar o HttpListener no modo não administrativo. Tudo o que você precisa fazer é conceder permissões para o URL específico. por exemplo

netsh http add urlacl url=http://+:80/MyUri user=DOMAIN\user

A documentação está aqui .

Darrel Miller
fonte
48
Isso é útil, mas, para ser completo, o URL especificado nesta linha de código: httpListener.Prefixes.Add("http://*:4444/");deve corresponder EXATAMENTE ao URL do netshcomando. Por exemplo, eu tinha httpListener.Prefixes.Add("http://127.0.0.1:80/");o mesmo netshcomando e o HttpListenerException ainda será lançado. Eu precisava mudar. httpListener.Prefixes.Add("http://+:80/");Obrigado pela ajuda, @Darrel Miller, porque você me colocou no caminho certo para descobrir isso!
Psyklopz 01/06/12
10
E não se esqueça da barra final se "MyUri" estiver vazio, caso contrário, você receberá um The parameter is incorrecterro. Exemplo:url=http://+:80/
Igor Brejc 16/05
14
Existe alguma maneira de fazer isso para um usuário não administrativo, mesmo para http://localhost:80/? Eu tenho um aplicativo de desktop que precisa receber uma solicitação em tal URL e parece uma pena exigir que um administrador o instale em 50 desktops, apenas para esse propósito.
John Saunders
5
Aparentemente não. Também não há menção de elevação ou privilégios administrativos exigidos na página de documentação. Portanto, é fácil supor que isso funcionará como um TCPListener "mais inteligente", quando, na realidade, há PRINCIPAIS motivos para não usá-lo - mas isso não está documentado.
David Ford
4
A execução do netsh requer admin; (
superfly71
27

Posso fazê-lo funcionar sem o modo de administrador? se sim como? Caso contrário, como posso fazer o aplicativo mudar para o modo de administrador depois de começar a executar?

Você não pode, tem que começar com privilégios elevados. Você pode reiniciá-lo com o runasverbo, que solicitará que o usuário mude para o modo de administrador

static void RestartAsAdmin()
{
    var startInfo = new ProcessStartInfo("yourApp.exe") { Verb = "runas" };
    Process.Start(startInfo);
    Environment.Exit(0);
}

EDIT: na verdade, isso não é verdade; O HttpListener pode ser executado sem privilégios elevados, mas você precisa dar permissão para o URL no qual deseja escutar. Veja a resposta de Darrel Miller para detalhes.

Thomas Levesque
fonte
Você pode explicar por que tenho que começar com privilégios elevados?
Randall Flagg
Você também precisa adicionar esta linha 'startInfo.UseShellExecute = false;' antes de 'Process.Start (startInfo);'
Randall Flagg
@ Randall: porque é assim que o Windows funciona ... um processo não pode mudar para o modo de administrador enquanto estiver em execução. Em relação ao UseShellExecute: depende do que você está executando. Testei meu código com "notepad.exe", ele funciona bem sem UseShellExecute = false
Thomas Levesque
Obrigado. Sobre o UseShellExecute: Tentei executar o código que publiquei. Outro problema é que, por algum motivo, ele me perguntou uma vez se eu quero executar como administrador e, em qualquer outro momento, depois disso, não pergunta. Eu reiniciei, depurado para garantir que ele vá lá e nada. alguma sugestão?
Randall Flagg
1
@ Thomas Não há nenhum problema ao executar o HttpListener no modo não administrativo.
Darrel Miller
23

Se você usar http://localhost:80/como prefixo, poderá ouvir solicitações http sem necessidade de privilégios administrativos.

Ehsan Mirsaeedi
fonte
2
Você pode postar algum código de exemplo? Eu apenas tentei isso http://localhost:80/e recebi um "Acesso negado".
John Saunders
2
Desculpe, isso não parece funcionar. Verifique se você está executando como administrador, o que não deve ser o caso normal.
precisa
se isso funcionasse, eu consideraria um bug do Windows. não importa o que você pensa sobre o Windows, presumo que este seja um nível muito básico que muito, muito provavelmente eles não deixaram de manipular corretamente.
hoijui
26
Portanto, mais de dois anos depois, isso funciona para mim agora no Windows Server 2008 R2 com o .NET framework 4.5. httpListener.Prefixes.Add("http://*:4444/");realmente mostra um Access Deniederro, mas httpListener.Prefixes.Add("http://localhost:4444/");funciona sem nenhum problema. Parece que a Microsoft foi excluída localhostdessas restrições. Curiosamente, httpListener.Prefixes.Add("http://127.0.0.1:4444/");ainda mostra um erro de Acesso Negado, então a única coisa que funciona élocalhost:{any port}
Tony
1
@ Tony, graças ao seu comentário foi realmente o mais valioso para mim. Eu tinha "127.0.0.1" em várias configurações. O controle de qualidade informou que não estava funcionando no Windows 8.1, mas estava no Windows 10 muito bem (e eu mesmo testei no Win10). Estranhamente, parece que a Microsoft concedeu a Todos que usem 127.0.0.1 no Win10, mas não o 8.1 (e presumivelmente versões anteriores), mas com certeza, o host local está bom. Obrigado
Adam Plocher
20

A sintaxe estava errada para mim, você deve incluir as aspas:

netsh http add urlacl url="http://+:4200/" user=everyone

caso contrário, recebi "O parâmetro está incorreto"

Dave
fonte
4
"O parâmetro está incorreto" também pode ser devolvido se você não especificar um à direita /na url
LostSalad
13

Caso você queira usar o sinalizador "usuário = Todos", é necessário ajustá-lo ao idioma do sistema. Em inglês, é como mencionado:

netsh http add urlacl url=http://+:80/ user=Everyone

Em alemão seria:

netsh http add urlacl url=http://+:80/ user=Jeder
Christoph Brückmann
fonte
1
Em sistemas de língua espanhola, fazer: user = Todos
Rodrigo Rodrigues
Além disso, eu precisava colocar o URL entre aspas, como mencionado em outra resposta.
Carsten Führmann 18/09/19
10

Como alternativa que não requer elevação ou netsh, você também pode usar o TcpListener, por exemplo.

A seguir, um trecho modificado desse exemplo: https://github.com/googlesamples/oauth-apps-for-windows/tree/master/OAuthDesktopApp

// Generates state and PKCE values.
string state = randomDataBase64url(32);
string code_verifier = randomDataBase64url(32);
string code_challenge = base64urlencodeNoPadding(sha256(code_verifier));
const string code_challenge_method = "S256";

// Creates a redirect URI using an available port on the loopback address.
var listener = new TcpListener(IPAddress.Loopback, 0);
listener.Start();
string redirectURI = string.Format("http://{0}:{1}/", IPAddress.Loopback, ((IPEndPoint)listener.LocalEndpoint).Port);
output("redirect URI: " + redirectURI);

// Creates the OAuth 2.0 authorization request.
string authorizationRequest = string.Format("{0}?response_type=code&scope=openid%20profile&redirect_uri={1}&client_id={2}&state={3}&code_challenge={4}&code_challenge_method={5}",
    authorizationEndpoint,
    System.Uri.EscapeDataString(redirectURI),
    clientID,
    state,
    code_challenge,
    code_challenge_method);

// Opens request in the browser.
System.Diagnostics.Process.Start(authorizationRequest);

// Waits for the OAuth authorization response.
var client = await listener.AcceptTcpClientAsync();

// Read response.
var response = ReadString(client);

// Brings this app back to the foreground.
this.Activate();

// Sends an HTTP response to the browser.
WriteStringAsync(client, "<html><head><meta http-equiv='refresh' content='10;url=https://google.com'></head><body>Please close this window and return to the app.</body></html>").ContinueWith(t =>
{
    client.Dispose();
    listener.Stop();

    Console.WriteLine("HTTP server stopped.");
});

// TODO: Check the response here to get the authorization code and verify the code challenge

Os métodos de leitura e gravação são:

private string ReadString(TcpClient client)
{
    var readBuffer = new byte[client.ReceiveBufferSize];
    string fullServerReply = null;

    using (var inStream = new MemoryStream())
    {
        var stream = client.GetStream();

        while (stream.DataAvailable)
        {
            var numberOfBytesRead = stream.Read(readBuffer, 0, readBuffer.Length);
            if (numberOfBytesRead <= 0)
                break;

            inStream.Write(readBuffer, 0, numberOfBytesRead);
        }

        fullServerReply = Encoding.UTF8.GetString(inStream.ToArray());
    }

    return fullServerReply;
}

private Task WriteStringAsync(TcpClient client, string str)
{
    return Task.Run(() =>
    {
        using (var writer = new StreamWriter(client.GetStream(), new UTF8Encoding(false)))
        {
            writer.Write("HTTP/1.0 200 OK");
            writer.Write(Environment.NewLine);
            writer.Write("Content-Type: text/html; charset=UTF-8");
            writer.Write(Environment.NewLine);
            writer.Write("Content-Length: " + str.Length);
            writer.Write(Environment.NewLine);
            writer.Write(Environment.NewLine);
            writer.Write(str);
        }
    });
}
Michael Olsen
fonte
Isso foi realmente útil, pois tivemos o problema HttpListener ao implementar um IBrowser para a biblioteca IdentityModel.OidcClient, portanto, um caso de uso muito semelhante ao descrito acima.
quer
1
Vale a pena notar que o código acima possui bugs. Primeiramente, usar um StreamWriter com UTF8 causa problemas em alguns navegadores, suponho que devido à saída de uma lista técnica ou algo parecido. Eu mudei para ASCII e está tudo bem. Também adicionei um código para aguardar a disponibilização dos dados para leitura no fluxo, pois às vezes ele não retornava dados.
Mackie
Obrigado @mackie - essa parte foi tirada diretamente da própria amostra da Microsoft. Eu acho que eles realmente não testaram corretamente. Se você pode editar minha resposta, vá em frente e corrija os métodos - caso contrário, talvez me envie as alterações e eu possa atualizá-las.
Michael Olsen
2
Em vez de usar ASCII, deve-se usar o construtor a seguir new UTF8Encoding(false), que desativa a emissão de uma lista técnica. Eu já mudei isso na resposta #
Sebastian
Prefiro esta solução à resposta aceita. Executar netsh parece um hack. Esta é uma solução programática sólida. Obrigado!
Jim Gomes
7

Você pode iniciar seu aplicativo como administrador se adicionar o Manifesto de Aplicativo ao seu projeto.

Basta adicionar novo item ao seu projeto e selecione "Arquivo de manifesto do aplicativo". Mude o <requestedExecutionLevel>elemento para:

<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
R.Titov
fonte
7

Por padrão, o Windows define o seguinte prefixo disponível para todos: http: // +: 80 / Temporary_Listen_Addresses /

Para que você possa registrar seu HttpListenervia:

Prefixes.Add("http://+:80/Temporary_Listen_Addresses/" + Guid.NewGuid().ToString("D") + "/";

Às vezes, isso causa problemas com softwares como o Skype, que tentam utilizar a porta 80 por padrão.

Sebastian
fonte
1
O Skype era o culpado. Mudei a porta para não ser 80 e funcionou.
GoTo
5
httpListener.Prefixes.Add("http://*:4444/");

você usa "*" para executar o seguinte cmd como admin

netsh http add urlacl url=http://*:4444/ user=username

não use +, deve usar *, porque você especifica *: 4444 ~.

https://msdn.microsoft.com/en-us/library/system.net.httplistener.aspx

NamedStar
fonte
Foi isso para mim. Funcionou para mim.Add("http://+:9000/")
John Gietzen
2

Eu também enfrentei um problema semelhante. Se você já reservou o URL, primeiro exclua o URL para executar no modo não administrador, caso contrário, ele falhará com o erro Acesso negado.

netsh http delete urlacl url=http://+:80
vivek nuna
fonte