代码之家  ›  专栏  ›  技术社区  ›  DanO Roman

将leet speak转换为纯文本

  •  10
  • DanO Roman  · 技术社区  · 16 年前

    我没那么时髦 L33 语言超越了我在维基百科上看到的。

    我确实需要在我们的密码强度验证工具中添加字典检查,而且由于leet speak只会在密码破解过程中添加少量开销,所以我想在对照字典检查输入之前对其进行去Leet验证。

    为了阐明这背后的原因:当需要在密码中添加符号时,许多用户只需对一个常见单词进行一些非常可预测的LEET替换,以满足数字和符号包含要求。因为它是如此的可预测性,所以与使用原始字典单词相比,这几乎不会给密码增加实际的复杂性。编辑

    不知道所有的规则,尤其是多字符替换,比如“w”的“//”,并且确信这是一个已经被很多次解决的问题,当然也包括开源项目。

    我在找代码示例,但到目前为止还没有找到。如果是 C.* 这将是一个额外的代码!但是任何通用语言的代码都会有帮助。

    另外,如果有一个可扩展的方法会很好,因为我理解这种方言的发展很快。随着规则的发展,在一年内加入一些规则是很好的。

    不,这不是我整个密码强度检查的基础。这只是我在这篇文章中寻求帮助的部分。因此,我们不会被密码和安全问题的其他元素分散注意力,让我描述一下与leet speak无关的密码问题:

    我们根据 NIST special publication 800-63 ,并要求策略可配置的等效度量值(例如56位)才能使密码有效。这仍然为字典单词留出了空间,而从熵的角度来看,这些单词并不是一个更好的简单字典单词。

    我只想告诉用户“p@s5w0rd”太接近一个字典单词,他们可能会找到一个更强大的密码。

    我知道还有很多安全方面的考虑,比如人们可以记住的密码和安全的密码之间的平衡。这不是那个问题。

    我所要问的是将L33T转换成纯文本,这应该和代码高尔夫一样有趣。有人看到任何代码示例吗?

    5 回复  |  直到 11 年前
        1
  •  10
  •   Sam Chad    12 年前

    还提供一些代码:

    String password  = @"\/\/4573Fu|_";
    Dictionary<string, string> leetRules = new Dictionary<string, string>();
    
    leetRules.Add("4", "A");
    leetRules.Add(@"/\", "A");
    leetRules.Add("@", "A");
    leetRules.Add("^", "A");
    
    leetRules.Add("13", "B");
    leetRules.Add("/3", "B");
    leetRules.Add("|3", "B");
    leetRules.Add("8", "B");
    
    leetRules.Add("><", "X");
    
    leetRules.Add("<", "C");
    leetRules.Add("(", "C");
    
    leetRules.Add("|)", "D");
    leetRules.Add("|>", "D");
    
    leetRules.Add("3", "E");
    
    leetRules.Add("6", "G");
    
    leetRules.Add("/-/", "H");
    leetRules.Add("[-]", "H");
    leetRules.Add("]-[", "H");
    
    leetRules.Add("!", "I");
    
    leetRules.Add("|_", "L");
    
    leetRules.Add("_/", "J");
    leetRules.Add("_|", "J");
    
    leetRules.Add("1", "L");
    
    leetRules.Add("0", "O");
    
    leetRules.Add("5", "S");
    
    leetRules.Add("7", "T");
    
    leetRules.Add(@"\/\/", "W");
    leetRules.Add(@"\/", "V");
    
    leetRules.Add("2", "Z");
    
    foreach (KeyValuePair<string,string> x in leetRules)
    {
        password = password.Replace(x.Key, x.Value);
    }
    
    MessageBox.Show(password.ToUpper());
    
        2
  •  13
  •   Fosco    16 年前

    我得说我认为这是个坏主意…如果你想让他们坚强,就提出更好的要求。必须至少为8个字符,包含大小写字母,包含至少一个数字和至少一个特殊字符。在禁用帐户之前实现最大授权失败计数器。完成后,你担心什么?

        3
  •  3
  •   samy    12 年前

    我发现这个问题很有趣,所以这里有一个作为智力练习的额外答案;因为leet speak不能映射到一个唯一的单词,所以您必须检查一个leet speak链可能提供的解码值。下面是一些示例代码:

    public class LeetSpeakDecoder
    {
        private Dictionary<string, IEnumerable<string>> Cache { get; set; }
        private Dictionary<string, string> Rules { get; set; }
    
        public LeetSpeakDecoder()
        {
            Cache = new Dictionary<string, IEnumerable<string>>();
            Rules = new Dictionary<string,string>();
    
            Rules.Add("4", "A");
            // add rules here...
        }
    
        public IEnumerable<string> Decode(string leet)
        {
            var list = new List<string>();
            if (Cache.ContainsKey(leet))
            {
                return Cache[leet];
            }
    
            DecodeOneCharacter(leet, list);
            DecodeMoreThanOneCharacter(leet, list);
            DecodeWholeWord(leet, list);
    
            list = list.Distinct().ToList();
    
            Cache.Add(leet, list);
            return list;
        }
    
        private void DecodeOneCharacter(string leet, List<string> list)
        {
            if (leet.Length == 1)
            {
                list.Add(leet);
            }
        }
    
        private void DecodeMoreThanOneCharacter(string leet, List<string> list)
        {
            if (leet.Length > 1)
            {   // we split the word in two parts and check how many variations each part will decode to
                for (var splitPoint = 1; splitPoint < leet.Length; splitPoint++)
                {
                    foreach (var leftPartDecoded in Decode(leet.Substring(0, splitPoint)))
                    {
                        foreach (var rightPartDecoded in Decode(leet.Substring(splitPoint)))
                        {
                            list.Add(leftPartDecoded + rightPartDecoded);
                        }
                    }
                }
            }
        }
    
        private void DecodeWholeWord(string leet, List<string> list)
        {
            if (Rules.ContainsKey(leet))
            {
                list.Add(Rules[leet]);
            }
        }
    }
    

    法典认为

    • 一个字符可以保持原样( DecodeOneCharacter )
    • 一个词必须通过该词所有可能的拆分的解码值的组合来解码。( DecodeMoreThanOneCharacter )
    • 必须根据规则直接解码单词( DecodeWholeWord )

    缓存非常有用,因为代码在跟踪哪些排列是无用的方面效率非常低:将“浪费”拆分为“w”和“asteful”或“wa”和“steful”将导致在右侧重复解码,最终在左侧重复解码。我没有太多的数据,但它经常会受到超过90%的译码的攻击;对于一个小的添加来说不算太差。

    由于它返回了所有可能的LEET解码字符串的组合,您可能想看看它的解决方案:例如,fosco的代码将“137M3P455”解码为“btmapass”,但您可能想知道它还翻译为“letmapass”,这会使您更加畏缩。

        4
  •  3
  •   Jason Coyne    11 年前

    基于上面Samy的回答,这里有一个更进一步的增强版本。它允许每个输入字符有多个输出规则,特别是为要从字符串中删除的所有非字母数字字符设置了规则。结果是,您可以发送经典的XKCD Comic密码tr0ub4dor&3,然后退出Troubador。

    我使用它的目的与OP基本相同,以确认提供给我的系统的包含高度安全的数据的密码不是基于字典单词。

    我正在获取解码函数的输出,并通过字典运行它。

      public class LeetSpeakDecoder
        {
            private Dictionary<string, IEnumerable<string>> Cache { get; set; }
            private Dictionary<string, List<string>> Rules  = new Dictionary<string, List<string>>();
    
            public void AddRule(string key, string value)
            {
                List<string> keyRules = null;
                if (Rules.ContainsKey(key))
                {
                    keyRules = Rules[key];
                }
                else
                {
                    keyRules = new List<string>();
                    Rules[key] = keyRules;
                }
                keyRules.Add(value);
            }
    
            public LeetSpeakDecoder()
            {
                Cache = new Dictionary<string, IEnumerable<string>>();
    
    
                AddRule("4", "A");
                AddRule("4", "a");
               AddRule(@"/\", "A");
               AddRule("@", "A");
               AddRule("^", "A");
               AddRule("13", "B");
               AddRule("/3", "B");
               AddRule("|3", "B");
               AddRule("8", "B");
               AddRule("><", "X");
               AddRule("<", "C");
               AddRule("(", "C");
               AddRule("|)", "D");
               AddRule("|>", "D");
               AddRule("3", "E");
               AddRule("6", "G");
               AddRule("/-/", "H");
               AddRule("[-]", "H");
               AddRule("]-[", "H");
               AddRule("!", "I");
               AddRule("|_", "L");
               AddRule("_/", "J");
               AddRule("_|", "J");
               AddRule("1", "L");
               AddRule("0", "O");
               AddRule("0", "o");
               AddRule("5", "S");
               AddRule("7", "T");
               AddRule(@"\/\/", "W");
               AddRule(@"\/", "V");
               AddRule("2", "Z");
    
                const string nonAlpha = @"0123456789!@#$%^&*()-_=+[]{}\|;:'<,>./?""";
                foreach (var currentChar in nonAlpha)
                {
                    AddRule(currentChar.ToString(), "");
                }
            }
    
            public IEnumerable<string> Decode(string leet)
            {
                var list = new List<string>();
                if (Cache.ContainsKey(leet))
                {
                    return Cache[leet];
                }
    
                DecodeOneCharacter(leet, list);
                DecodeMoreThanOneCharacter(leet, list);
                DecodeWholeWord(leet, list);
    
                list = list.Distinct().ToList();
    
                Cache.Add(leet, list);
                return list;
            }
    
            private void DecodeOneCharacter(string leet, List<string> list)
            {
                if (leet.Length == 1)
                {
                    list.Add(leet);
                }
            }
    
            private void DecodeMoreThanOneCharacter(string leet, List<string> list)
            {
                if (leet.Length > 1)
                {   // we split the word in two parts and check how many variations each part will decode to
                    for (var splitPoint = 1; splitPoint < leet.Length; splitPoint++)
                    {
                        foreach (var leftPartDecoded in Decode(leet.Substring(0, splitPoint)))
                        {
                            foreach (var rightPartDecoded in Decode(leet.Substring(splitPoint)))
                            {
                                list.Add(leftPartDecoded + rightPartDecoded);
                            }
                        }
                    }
                }
            }
    
            private void DecodeWholeWord(string leet, List<string> list)
            {
                if (Rules.ContainsKey(leet))
                {
                    foreach (var ruleValue in Rules[leet])
                    {
                        list.Add(ruleValue);
                    }
    
                }
            }
        }
    

    这是我的输出

    Tr0ub4dor&3
    Tr0ub4dor&E
    Tr0ub4dor&
    Tr0ub4dor3
    Tr0ub4dorE
    Tr0ub4dor
    Tr0ubAdor&3
    Tr0ubAdor&E
    Tr0ubAdor&
    Tr0ubAdor3
    Tr0ubAdorE
    Tr0ubAdor
    Tr0ubador&3
    Tr0ubador&E
    Tr0ubador&
    Tr0ubador3
    Tr0ubadorE
    Tr0ubador
    Tr0ubdor&3
    Tr0ubdor&E
    Tr0ubdor&
    Tr0ubdor3
    Tr0ubdorE
    Tr0ubdor
    TrOub4dor&3
    TrOub4dor&E
    TrOub4dor&
    TrOub4dor3
    TrOub4dorE
    TrOub4dor
    TrOubAdor&3
    TrOubAdor&E
    TrOubAdor&
    TrOubAdor3
    TrOubAdorE
    TrOubAdor
    TrOubador&3
    TrOubador&E
    TrOubador&
    TrOubador3
    TrOubadorE
    TrOubador
    TrOubdor&3
    TrOubdor&E
    TrOubdor&
    TrOubdor3
    TrOubdorE
    TrOubdor
    Troub4dor&3
    Troub4dor&E
    Troub4dor&
    Troub4dor3
    Troub4dorE
    Troub4dor
    TroubAdor&3
    TroubAdor&E
    TroubAdor&
    TroubAdor3
    TroubAdorE
    TroubAdor
    Troubador&3
    Troubador&E
    Troubador&
    Troubador3
    TroubadorE
    Troubador
    Troubdor&3
    Troubdor&E
    Troubdor&
    Troubdor3
    TroubdorE
    Troubdor
    Trub4dor&3
    Trub4dor&E
    Trub4dor&
    Trub4dor3
    Trub4dorE
    Trub4dor
    TrubAdor&3
    TrubAdor&E
    TrubAdor&
    TrubAdor3
    TrubAdorE
    TrubAdor
    Trubador&3
    Trubador&E
    Trubador&
    Trubador3
    TrubadorE
    Trubador
    Trubdor&3
    Trubdor&E
    Trubdor&
    Trubdor3
    TrubdorE
    Trubdor
    
        5
  •  2
  •   Jay    16 年前

    为什么不实现一个函数来创建 "pronounceable" passwords 并要求用户使用它们?看起来工作更少,安全性更好。