AntiForgeryToken недействителен после входа в систему - PullRequest
13 голосов
/ 01 февраля 2012

У меня есть форма, которую пользователь может публиковать без входа в систему. Если, однако, его электронная почта распознана, требуется пароль.Форма пароля проверяется через Ajax, и в случае успеха отправляется основная форма.Обе формы требуют действительного AntiForgeryToken.

Подвох заключается в том, что проверка паролем как побочный продукт также подписывает пользователя (требование от клиента).Это делает маркер недействительным, и основная форма не может быть отправлена.

Я попытался программно сгенерировать новый токен, но не могу заставить его работать.

Есть идеи, как решить эту проблему?

Окончательное решение

Я нашел этот вопрос, который будет полезен при наборе отражения.Тем не менее, и это главная причина, по которой при обычных обстоятельствах вам следует избегать взлома внутренних типов, заключается в том, что типы перетаскиваются между сборками и между выпусками.Как подсказывает Бетти, используйте ILSpy для поиска вещей.

Это окончательный код.

if (signIn)
    FormsAuth.SignIn(user.Email, false);


var mvcAssembly = typeof(AntiForgery).Assembly;
var afdType = mvcAssembly.GetType("System.Web.Helpers.AntiForgeryData");
string fieldName = Convert.ToString(afdType.InvokeMember(
    "GetAntiForgeryTokenName",
    BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.InvokeMethod,
    null,
    null,
    new object[] { null }));

var serializerType = mvcAssembly.GetType("System.Web.Helpers.AntiForgeryDataSerializer");
var serializerCtor = serializerType.GetConstructor(new Type[0]);
object serializer = serializerCtor.Invoke(new object[0]);


string text = HttpContext.Request.Form[fieldName];
object antiForgeryData = serializerType.InvokeMember("Deserialize", BindingFlags.InvokeMethod, null, serializer, new object[] { text });

afdType.GetProperty("Username").SetValue(antiForgeryData, 
    signIn ? user.Email : string.Empty, 
    null);

string newToken = Convert.ToString(serializerType.InvokeMember(
    "Serialize",
    BindingFlags.InvokeMethod,
    null,
    serializer,
    new object[] { antiForgeryData }));

return Content(JsonConvert.SerializeObject(new
                                                {
                                                    success = true,
                                                    newAntiForgeryToken = newToken
                                                }), Constant.JsonContentType);

Обновление для веб-страниц 2.0

  var mvcAssembly = typeof(AntiForgery).Assembly;
        var afdType = mvcAssembly.GetType("System.Web.Helpers.AntiXsrf.AntiForgeryToken");
        //string fieldName = Convert.ToString(afdType.InvokeMember(
        //    "GetAntiForgeryTokenName",
        //    BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.InvokeMethod,
        //    null,
        //    null,
        //    new object[] { null }));

        string fieldName = "__RequestVerificationToken";

        var serializerType = mvcAssembly.GetType("System.Web.Helpers.AntiXsrf.AntiForgeryTokenSerializer");
        var serializerCtor = serializerType.GetConstructor(new Type[0]);
        object serializer = serializerCtor.Invoke(new object[0]);


        string text = HttpContext.Request.Form[fieldName];
        string newToken = String.Empty;

        if (!String.IsNullOrEmpty(text))
        {
            object antiForgeryToken = serializerType.InvokeMember("Deserialize", BindingFlags.InvokeMethod, null,
                                                                 serializer, new object[] { text });

            afdType.GetProperty("Username").SetValue(antiForgeryToken,
                                                     signIn ? user.Email : string.Empty,
                                                     null);

            newToken = Convert.ToString(serializerType.InvokeMember(
                "Serialize",
                BindingFlags.InvokeMethod,
                null,
                serializer,
                new[] { antiForgeryToken }));
        }

Ответы [ 2 ]

4 голосов
/ 04 февраля 2012

Текущий пользователь сохраняется в токене защиты от подделки в данных формы и сравнивается с текущим пользователем при обратной передаче.

Вы должны быть в состоянии извлечь токен формы при обратной передаче таким же образом, как ФилХаак делает это в этом посте .

Затем используйте класс AntiForgeryDataSerializer для десериализации токена, обновления текущего пользователя, повторной сериализации и помещения его обратно в форму перед проверкой.Или полностью замените метод validate своим собственным атрибутом.

В качестве альтернативы, вместо обновления его при обратной передаче основных форм, вы можете попробовать отправить обновленный токен обратно с запросом пароля ajax и обновить форму.В любом случае базовый подход один и тот же: десериализация, обновление пользователя, сериализация, замена токена.

string antiForgeryTokenName = AntiForgeryData.GetAntiForgeryTokenName(null);
string text = context.Request.Form[antiForgeryTokenName];
AntiForgeryDataSerializer serializer = new AntiForgeryDataSerializer();

AntiForgeryData antiForgeryData = serializer.Deserialize(text); 
antiForgeryData.Username = AntiForgeryData.GetUsername(context.User);
string newToken = serializer.Serialize(antiForgeryData);    

AntiForgeryDataSerializer и AntiForgeryData являются внутренними классами, поэтому вам придется использовать некоторые базовые отражения для вызова методов для них.

1 голос
/ 10 января 2013

Обновлен окончательный ответ для учета изменений в конструкторе AntiForgeryTokenSerializer:

    const string serializerAssembly = "System.Web.Helpers.AntiXsrf.AntiForgeryTokenSerializer";
    const string cryptoAssembly = "System.Web.Helpers.AntiXsrf.MachineKey40CryptoSystem";
    const string token = "System.Web.Helpers.AntiXsrf.AntiForgeryToken";
    const string fieldName = "__RequestVerificationToken";

    Assembly mvcAssembly = typeof (AntiForgery).Assembly;
    Type afdType = mvcAssembly.GetType(token);

    Type serializerType = mvcAssembly.GetType(serializerAssembly);
    Type cryptoType = mvcAssembly.GetType(cryptoAssembly);
    var constructors = serializerType.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
    ConstructorInfo cryptoConstructor = cryptoType.GetConstructor(new Type[0]);
    var crypto = cryptoConstructor.Invoke(new object[0]);
    object serializer = constructors[0].Invoke(new object[] { crypto });

    string text = currentContext.Request.Form[fieldName];
    string newToken = String.Empty;

    if (!String.IsNullOrEmpty(text))
    {
        object antiForgeryToken = serializerType.InvokeMember("Deserialize", BindingFlags.InvokeMethod, null,
                                                              serializer, new object[] {text});

        afdType.GetProperty("Username").SetValue(antiForgeryToken,
                                                 signIn ? user.Email : string.Empty,
                                                 null);

        newToken = Convert.ToString(serializerType.InvokeMember(
            "Serialize",
            BindingFlags.InvokeMethod,
            null,
            serializer,
            new[] {antiForgeryToken}));
    }

    return newToken;
...