Du hast in beiden Definitionen kleine aber wesentliche Fehler. In der Tokenlist-Definition hast Du folgende Zeile:
    \tl_set:Nn \l_zeichen_tl {\str_item_ignore_spaces:nn {#1} {##1}}
Wenn Du die damit definierte Tokenlist mit `\tl_show:N \l_zeichen_tl` inspizierst, zeigt es Dir z.B.
    > \l_zeichen_tl=\str_item_ignore_spaces:nn {12ab}{1}
Ändere die Zeile in
    \tl_set:Nx \l_zeichen_tl {\str_item_ignore_spaces:nn {#1} {##1}}
und daraus wird
    > \l_zeichen_tl=1
Das reicht aber noch nicht. `\tl_case:Nn` testet gegen den Inhalt von Variablen, nicht gegen einzelne Token. Du musst also passende Testvariablen definieren
    \tl_const:Nn \c_zeichen_case_a_tl {0}
    \tl_const:Nn \c_zeichen_case_b_tl {1}
    \tl_const:Nn \c_zeichen_case_c_tl {a}
    \tl_const:Nn \c_zeichen_case_d_tl {b}
und verwenden:
    \tl_case:NnF \l_zeichen_tl
      {
        \c_zeichen_case_a_tl {}
        \c_zeichen_case_b_tl {}
        \c_zeichen_case_c_tl {}
        \c_zeichen_case_d_tl {}
      }
      {...}
Das reicht immer noch nicht: in Tokenlists hängt ein Match auch vom Kategoriecode der Zeichen ab. Mit
    \tl_set:Nx \l_zeichen_tl {\str_item_ignore_spaces:nn {#1} {##1}}
ensteht eine Tokenliste, in der alle Zeichen Kategoriecode 12 haben. In den Testvariablen haben sie das mit obiger Definition nicht. Darum ändern wir noch mal:
    \tl_set:Nx \l_zeichen_tl {\tl_item:nn {#1} {##1}}
Und jetzt klappt der Test.
----------
In der String-Variante hast Du ähnliche Fehler:
    \str_set:Nn \l_zeichen_str {\str_item_ignore_spaces:nn {#1} {##1}}
Ändere das in
    \str_set:Nx \l_zeichen_str {\str_item_ignore_spaces:nn {#1} {##1}}
Außerdem hast Du
    \str_case:nnF {\l_zeichen_str} ...
Das vergleicht aber *nicht* den Inhalt der Variablen `\l_zeichen_str` mit den verschiedenen Fällen, sondern den String `\ l _ z e i c h e n _ s t r` (ohne Leerzeichen). Du brauchst die Variante
    \str_case:VnF \l_zeichen_str ...
die Du am besten mit
    \cs_generate_variant:Nn \str_case:nnF {V}
bereitstellst.
----------
Mit diesen Korrekturen funktioniert Dein Code:
    \documentclass{article}
    \usepackage[check-declarations]{expl3}
    \usepackage{xparse}
    
    \ExplSyntaxOn
    
    \int_new:N \l_zeichenzahl_int
    \tl_new:N \l_zeichen_tl
    \str_new:N \l_zeichen_str
    
    \tl_const:Nn \c_zeichen_case_a_tl {0}
    \tl_const:Nn \c_zeichen_case_b_tl {1}
    \tl_const:Nn \c_zeichen_case_c_tl {a}
    \tl_const:Nn \c_zeichen_case_d_tl {b}
    
    \cs_new_protected:Npn \test_mit_tl:n #1
      {
        \int_set:Nn \l_zeichenzahl_int {\str_count_ignore_spaces:n {#1}}
        \cs_set:Npn \zeichen_pruefen:n ##1
          {
            \tl_set:Nx \l_zeichen_tl {\tl_item:nn {#1} {##1}}
            \tl_case:NnF \l_zeichen_tl
              {
                \c_zeichen_case_a_tl {}
                \c_zeichen_case_b_tl {}
                \c_zeichen_case_c_tl {}
                \c_zeichen_case_d_tl {}
              }
              {\PackageWarning{noname}{Falsches~Zeichen~(tl):~\l_zeichen_tl}}
          }
        \int_step_function:nnnN {1} {1} {\l_zeichenzahl_int} \zeichen_pruefen:n
      }
    
    \cs_new_protected:Npn \test_mit_str:n #1
      {
        \int_set:Nn \l_zeichenzahl_int {\str_count_ignore_spaces:n {#1}}
        \cs_set:Npn \zeichen_pruefen:n ##1
          {
            \str_set:Nx \l_zeichen_str {\str_item_ignore_spaces:nn {#1} {##1}}
            \str_case:VnF {\l_zeichen_str}
              {
                {0} {}
                {1} {}
                {a} {}
                {b} {}
              }
              {\PackageWarning{noname}{Falsches~Zeichen~(str):~\l_zeichen_str}}
          }
        \int_step_function:nnnN {1} {1} {\l_zeichenzahl_int} \zeichen_pruefen:n
      }
    \cs_generate_variant:Nn \str_case:nnF {V}
    
    \NewDocumentCommand \Befehl {m}
      {
        \test_mit_tl:n {#1}
        \test_mit_str:n {#1}
      }
    
    \ExplSyntaxOff
    
    \begin{document}
    
    \Befehl{12ab}
    
    \Befehl{12 ab}
    
    \end{document}
Im Log:
    Package noname Warning: Falsches Zeichen (tl): 2 on input line 63.
    
    
    Package noname Warning: Falsches Zeichen (str): 2 on input line 63.
    
    
    Package noname Warning: Falsches Zeichen (tl): 2 on input line 65.
    
    
    Package noname Warning: Falsches Zeichen (str): 2 on input line 65.
----------
Das ganze geht aber auch erheblich leichter:
    \documentclass{article}
    \usepackage{expl3,xparse}
    
    \ExplSyntaxOn
    \tl_new:N \l_check_input_allowed_tl
    \tl_set:Nn \l_check_input_allowed_tl {01ab}
    \seq_new:N \l_check_input_wrong_seq
    
    \cs_new_protected:Npn \check_input:n #1
      {
        \seq_clear:N \l_check_input_wrong_seq
        \tl_map_inline:nn {#1}
          {
            \tl_if_in:NnF \l_check_input_allowed_tl {##1}
              { \PackageWarning {noname} {Falsche~ Zeichen:~ `##1'} }
          }
      }
    
    \NewDocumentCommand \checkinput {m}
      { \check_input:n {#1} }
    \ExplSyntaxOff
    
    \begin{document}
    
    \checkinput{}    
    \checkinput{foo}
    \checkinput{ab10}
    \checkinput{a1b0}
    \checkinput{a1c0}
    \checkinput{ab 10}
    
    \end{document}
Im Log:
    Package noname Warning: Falsche Zeichen: `f' on input line 26.
    
    
    Package noname Warning: Falsche Zeichen: `o' on input line 26.
    
    
    Package noname Warning: Falsche Zeichen: `o' on input line 26.
    
    
    Package noname Warning: Falsche Zeichen: `c' on input line 29.